Merge pull request 'mycelium-rhai' (#9) from mycelium-rhai into main
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
Reviewed-on: #9
This commit is contained in:
commit
887e66bb17
@ -47,6 +47,9 @@ tokio-postgres = "0.7.8" # Async PostgreSQL client
|
|||||||
tokio-test = "0.4.4"
|
tokio-test = "0.4.4"
|
||||||
uuid = { version = "1.16.0", features = ["v4"] }
|
uuid = { version = "1.16.0", features = ["v4"] }
|
||||||
zinit-client = { git = "https://github.com/threefoldtech/zinit", branch = "json_rpc", package = "zinit-client" }
|
zinit-client = { git = "https://github.com/threefoldtech/zinit", branch = "json_rpc", package = "zinit-client" }
|
||||||
|
reqwest = { version = "0.12.15", features = ["json"] }
|
||||||
|
urlencoding = "2.1.3"
|
||||||
|
|
||||||
# Optional features for specific OS functionality
|
# Optional features for specific OS functionality
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
nix = "0.30.1" # Unix-specific functionality
|
nix = "0.30.1" # Unix-specific functionality
|
||||||
|
386
docs/docs/rhai/mycelium_tutorial.md
Normal file
386
docs/docs/rhai/mycelium_tutorial.md
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
# Mycelium Tutorial for Rhai
|
||||||
|
|
||||||
|
This tutorial explains how to use the Mycelium networking functionality in Rhai scripts. Mycelium is a peer-to-peer networking system that allows nodes to communicate with each other, and the Rhai bindings provide an easy way to interact with Mycelium from your scripts.
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The Mycelium module for Rhai provides the following capabilities:
|
||||||
|
|
||||||
|
- Getting node information
|
||||||
|
- Managing peers (listing, adding, removing)
|
||||||
|
- Viewing routing information
|
||||||
|
- Sending and receiving messages between nodes
|
||||||
|
|
||||||
|
This tutorial will walk you through using these features with example scripts.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before using the Mycelium functionality in Rhai, you need:
|
||||||
|
|
||||||
|
1. A running Mycelium node accessible via HTTP
|
||||||
|
> See https://github.com/threefoldtech/mycelium
|
||||||
|
2. The Rhai runtime with Mycelium module enabled
|
||||||
|
|
||||||
|
## Basic Mycelium Operations
|
||||||
|
|
||||||
|
Let's start by exploring the basic operations available in Mycelium using the `mycelium_basic.rhai` example.
|
||||||
|
|
||||||
|
### Getting Node Information
|
||||||
|
|
||||||
|
To get information about your Mycelium node:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:8989";
|
||||||
|
|
||||||
|
// Get node information
|
||||||
|
print("Getting node information:");
|
||||||
|
try {
|
||||||
|
let node_info = mycelium_get_node_info(api_url);
|
||||||
|
print(`Node subnet: ${node_info.nodeSubnet}`);
|
||||||
|
print(`Node public key: ${node_info.nodePubkey}`);
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error getting node info: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Sets the API URL for your Mycelium node
|
||||||
|
2. Calls `mycelium_get_node_info()` to retrieve information about the node
|
||||||
|
3. Prints the node's subnet and public key
|
||||||
|
|
||||||
|
### Managing Peers
|
||||||
|
|
||||||
|
#### Listing Peers
|
||||||
|
|
||||||
|
To list all peers connected to your Mycelium node:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// List all peers
|
||||||
|
print("\nListing all peers:");
|
||||||
|
try {
|
||||||
|
let peers = mycelium_list_peers(api_url);
|
||||||
|
|
||||||
|
if peers.is_empty() {
|
||||||
|
print("No peers connected.");
|
||||||
|
} else {
|
||||||
|
for peer in peers {
|
||||||
|
print(`Peer Endpoint: ${peer.endpoint.proto}://${peer.endpoint.socketAddr}`);
|
||||||
|
print(` Type: ${peer.type}`);
|
||||||
|
print(` Connection State: ${peer.connectionState}`);
|
||||||
|
print(` Bytes sent: ${peer.txBytes}`);
|
||||||
|
print(` Bytes received: ${peer.rxBytes}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error listing peers: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Calls `mycelium_list_peers()` to get all connected peers
|
||||||
|
2. Iterates through the peers and prints their details
|
||||||
|
|
||||||
|
#### Adding a Peer
|
||||||
|
|
||||||
|
To add a new peer to your Mycelium node:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// Add a new peer
|
||||||
|
print("\nAdding a new peer:");
|
||||||
|
let new_peer_address = "tcp://65.21.231.58:9651";
|
||||||
|
try {
|
||||||
|
let result = mycelium_add_peer(api_url, new_peer_address);
|
||||||
|
print(`Peer added: ${result.success}`);
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error adding peer: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Specifies a peer address to add
|
||||||
|
2. Calls `mycelium_add_peer()` to add the peer to your node
|
||||||
|
3. Prints whether the operation was successful
|
||||||
|
|
||||||
|
#### Removing a Peer
|
||||||
|
|
||||||
|
To remove a peer from your Mycelium node:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// Remove a peer
|
||||||
|
print("\nRemoving a peer:");
|
||||||
|
let peer_id = "tcp://65.21.231.58:9651"; // This is the peer we added earlier
|
||||||
|
try {
|
||||||
|
let result = mycelium_remove_peer(api_url, peer_id);
|
||||||
|
print(`Peer removed: ${result.success}`);
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error removing peer: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Specifies the peer ID to remove
|
||||||
|
2. Calls `mycelium_remove_peer()` to remove the peer
|
||||||
|
3. Prints whether the operation was successful
|
||||||
|
|
||||||
|
### Viewing Routing Information
|
||||||
|
|
||||||
|
#### Listing Selected Routes
|
||||||
|
|
||||||
|
To list the selected routes in your Mycelium node:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// List selected routes
|
||||||
|
print("\nListing selected routes:");
|
||||||
|
try {
|
||||||
|
let routes = mycelium_list_selected_routes(api_url);
|
||||||
|
|
||||||
|
if routes.is_empty() {
|
||||||
|
print("No selected routes.");
|
||||||
|
} else {
|
||||||
|
for route in routes {
|
||||||
|
print(`Subnet: ${route.subnet}`);
|
||||||
|
print(` Next hop: ${route.nextHop}`);
|
||||||
|
print(` Metric: ${route.metric}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error listing routes: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Calls `mycelium_list_selected_routes()` to get all selected routes
|
||||||
|
2. Iterates through the routes and prints their details
|
||||||
|
|
||||||
|
#### Listing Fallback Routes
|
||||||
|
|
||||||
|
To list the fallback routes in your Mycelium node:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// List fallback routes
|
||||||
|
print("\nListing fallback routes:");
|
||||||
|
try {
|
||||||
|
let routes = mycelium_list_fallback_routes(api_url);
|
||||||
|
|
||||||
|
if routes.is_empty() {
|
||||||
|
print("No fallback routes.");
|
||||||
|
} else {
|
||||||
|
for route in routes {
|
||||||
|
print(`Subnet: ${route.subnet}`);
|
||||||
|
print(` Next hop: ${route.nextHop}`);
|
||||||
|
print(` Metric: ${route.metric}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error listing fallback routes: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Calls `mycelium_list_fallback_routes()` to get all fallback routes
|
||||||
|
2. Iterates through the routes and prints their details
|
||||||
|
|
||||||
|
## Sending Messages
|
||||||
|
|
||||||
|
Now let's look at how to send messages using the `mycelium_send_message.rhai` example.
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:1111";
|
||||||
|
|
||||||
|
// Send a message
|
||||||
|
print("\nSending a message:");
|
||||||
|
let destination = "5af:ae6b:dcd8:ffdb:b71:7dde:d3:1033"; // Replace with the actual destination IP address
|
||||||
|
let topic = "test_topic";
|
||||||
|
let message = "Hello from Rhai sender!";
|
||||||
|
let deadline_secs = -10; // Seconds we wait for a reply
|
||||||
|
|
||||||
|
try {
|
||||||
|
print(`Attempting to send message to ${destination} on topic '${topic}'`);
|
||||||
|
let result = mycelium_send_message(api_url, destination, topic, message, deadline_secs);
|
||||||
|
print(`result: ${result}`);
|
||||||
|
print(`Message sent: ${result.success}`);
|
||||||
|
if result.id != "" {
|
||||||
|
print(`Message ID: ${result.id}`);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error sending message: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Sets the API URL for your Mycelium node
|
||||||
|
2. Specifies the destination IP address, topic, message content, and deadline
|
||||||
|
3. Calls `mycelium_send_message()` to send the message
|
||||||
|
4. Prints the result, including the message ID if successful
|
||||||
|
|
||||||
|
### Important Parameters for Sending Messages
|
||||||
|
|
||||||
|
- `api_url`: The URL of your Mycelium node's API
|
||||||
|
- `destination`: The IP address of the destination node
|
||||||
|
- `topic`: The topic to send the message on (must match what the receiver is listening for)
|
||||||
|
- `message`: The content of the message
|
||||||
|
- `deadline_secs`: Time in seconds to wait for a reply. Use a negative value if you don't want to wait for a reply.
|
||||||
|
|
||||||
|
## Receiving Messages
|
||||||
|
|
||||||
|
Now let's look at how to receive messages using the `mycelium_receive_message.rhai` example.
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:2222";
|
||||||
|
|
||||||
|
// Receive messages
|
||||||
|
print("\nReceiving messages:");
|
||||||
|
let receive_topic = "test_topic";
|
||||||
|
let wait_deadline_secs = 100;
|
||||||
|
|
||||||
|
print(`Listening for messages on topic '${receive_topic}'...`);
|
||||||
|
try {
|
||||||
|
let messages = mycelium_receive_messages(api_url, receive_topic, wait_deadline_secs);
|
||||||
|
|
||||||
|
if messages.is_empty() {
|
||||||
|
// print("No new messages received in this poll.");
|
||||||
|
} else {
|
||||||
|
print("Received a message:");
|
||||||
|
print(` Message id: ${messages.id}`);
|
||||||
|
print(` Message from: ${messages.srcIp}`);
|
||||||
|
print(` Topic: ${messages.topic}`);
|
||||||
|
print(` Payload: ${messages.payload}`);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error receiving messages: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Finished attempting to receive messages.");
|
||||||
|
```
|
||||||
|
|
||||||
|
This code:
|
||||||
|
1. Sets the API URL for your Mycelium node
|
||||||
|
2. Specifies the topic to listen on and how long to wait for messages
|
||||||
|
3. Calls `mycelium_receive_messages()` to receive messages
|
||||||
|
4. Processes and prints any received messages
|
||||||
|
|
||||||
|
### Important Parameters for Receiving Messages
|
||||||
|
|
||||||
|
- `api_url`: The URL of your Mycelium node's API
|
||||||
|
- `receive_topic`: The topic to listen for messages on (must match what the sender is using)
|
||||||
|
- `wait_deadline_secs`: Time in seconds to wait for messages to arrive. The function will block for this duration if no messages are immediately available.
|
||||||
|
|
||||||
|
## Complete Messaging Example
|
||||||
|
|
||||||
|
To set up a complete messaging system, you would typically run two instances of Mycelium (node A sender, node B receiver).
|
||||||
|
|
||||||
|
1. Run the `mycelium_receive_message.rhai` script to listen for messages. **Fill in the API address of node B**.
|
||||||
|
2. Run the `mycelium_send_message.rhai` script to send messages. **Fill in the API address of node A, and fill in the overlay address of node B as destination**.
|
||||||
|
|
||||||
|
### Setting Up the Receiver
|
||||||
|
|
||||||
|
First, start a Mycelium node and run the receiver script:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:2222"; // Your receiver node's API URL
|
||||||
|
|
||||||
|
// Receive messages
|
||||||
|
let receive_topic = "test_topic";
|
||||||
|
let wait_deadline_secs = 100; // Wait up to 100 seconds for messages
|
||||||
|
|
||||||
|
print(`Listening for messages on topic '${receive_topic}'...`);
|
||||||
|
try {
|
||||||
|
let messages = mycelium_receive_messages(api_url, receive_topic, wait_deadline_secs);
|
||||||
|
|
||||||
|
if messages.is_empty() {
|
||||||
|
print("No new messages received in this poll.");
|
||||||
|
} else {
|
||||||
|
print("Received a message:");
|
||||||
|
print(` Message id: ${messages.id}`);
|
||||||
|
print(` Message from: ${messages.srcIp}`);
|
||||||
|
print(` Topic: ${messages.topic}`);
|
||||||
|
print(` Payload: ${messages.payload}`);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error receiving messages: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setting Up the Sender
|
||||||
|
|
||||||
|
Then, on another Mycelium node, run the sender script:
|
||||||
|
|
||||||
|
```rhai
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:1111"; // Your sender node's API URL
|
||||||
|
|
||||||
|
// Send a message
|
||||||
|
let destination = "5af:ae6b:dcd8:ffdb:b71:7dde:d3:1033"; // The receiver node's IP address
|
||||||
|
let topic = "test_topic"; // Must match the receiver's topic
|
||||||
|
let message = "Hello from Rhai sender!";
|
||||||
|
let deadline_secs = -10; // Don't wait for a reply
|
||||||
|
|
||||||
|
try {
|
||||||
|
print(`Attempting to send message to ${destination} on topic '${topic}'`);
|
||||||
|
let result = mycelium_send_message(api_url, destination, topic, message, deadline_secs);
|
||||||
|
print(`Message sent: ${result.success}`);
|
||||||
|
if result.id != "" {
|
||||||
|
print(`Message ID: ${result.id}`);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error sending message: ${err}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: setting up 2 different Mycelium peers on same the host and sending/receiving a message
|
||||||
|
|
||||||
|
#### Obtain Mycelium
|
||||||
|
|
||||||
|
- Download the latest Mycelium binary from https://github.com/threefoldtech/mycelium/releases/
|
||||||
|
- Or compile from source
|
||||||
|
|
||||||
|
#### Setup
|
||||||
|
- Create two different private key files. Each key file should contain exactely 32 bytes. In this example we'll save these files as `sender.bin` and `receiver.bin`. Note: generate your own 32-byte key files, the values below are just used as examples.
|
||||||
|
> `echo '9f3d72c1a84be6f027bba94cde015ee839cedb2ac4f2822bfc94449e3e2a1c6a' > sender.bin`
|
||||||
|
|
||||||
|
> `echo 'e81c5a76f42bd9a3c73fe0bb2196acdfb6348e99d0b01763a2e57ce3a4e8f5dd' > receiver.bin`
|
||||||
|
|
||||||
|
#### Start the nodes
|
||||||
|
- **Sender**: this node will have the API server hosted on `127.0.0.1:1111` and the JSON-RPC server on `127.0.0.1:8991`.
|
||||||
|
> `sudo ./mycelium --key-file sender.bin --disable-peer-discovery --disable-quic --no-tun --api-addr 127.0.0.1:1111 --jsonrpc-addr 127.0.0.1:8991`
|
||||||
|
|
||||||
|
- **Receiver**: this node will have the API server hosted on `127.0.0.1:2222` and the JSON-RPC server on `127.0.0.1:8992`.
|
||||||
|
> `sudo ./mycelium --key-file receiver.bin --disable-peer-discovery --disable-quic --no-tun --api-addr 127.0.0.1:2222 --jsonrpc-addr 127.0.0.1:8992 --peers tcp://<UNDERLAY_IP_SENDER>:9651`
|
||||||
|
- Obtain the Mycelium overlay IP by running `./mycelium --key-file receiver.bin --api-addr 127.0.0.1:2222 inspect`. **Replace this IP as destination in the [mycelium_send_message.rhai](../../../examples/mycelium/mycelium_send_message.rhai) example**.
|
||||||
|
|
||||||
|
#### Execute the examples
|
||||||
|
- First build by executing `./build_herdo.sh` from the SAL root directory
|
||||||
|
- `cd target/debug`
|
||||||
|
|
||||||
|
- Run the sender script: `sudo ./herodo --path ../../examples/mycelium/mycelium_send_message.rhai`
|
||||||
|
```
|
||||||
|
Executing: ../../examples/mycelium/mycelium_send_message.rhai
|
||||||
|
|
||||||
|
Sending a message:
|
||||||
|
Attempting to send message to 50e:6d75:4568:366e:f75:2ac3:bbb1:3fdd on topic 'test_topic'
|
||||||
|
result: #{"id": "bfd47dc689a7b826"}
|
||||||
|
Message sent:
|
||||||
|
Message ID: bfd47dc689a7b826
|
||||||
|
Script executed successfull
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run the receiver script: `sudo ./herodo --path ../../examples/mycelium/mycelium_receive_message.rhai`
|
||||||
|
```
|
||||||
|
Executing: ../../examples/mycelium/mycelium_receive_message.rhai
|
||||||
|
|
||||||
|
Receiving messages:
|
||||||
|
Listening for messages on topic 'test_topic'...
|
||||||
|
Received a message:
|
||||||
|
Message id: bfd47dc689a7b826
|
||||||
|
Message from: 45d:26e1:a413:9d08:80ce:71c6:a931:4315
|
||||||
|
Topic: dGVzdF90b3BpYw==
|
||||||
|
Payload: SGVsbG8gZnJvbSBSaGFpIHNlbmRlciE=
|
||||||
|
Finished attempting to receive messages.
|
||||||
|
Script executed successfully
|
||||||
|
```
|
||||||
|
> Decoding the payload `SGVsbG8gZnJvbSBSaGFpIHNlbmRlciE=` results in the expected `Hello from Rhai sender!` message. Mission succesful!
|
||||||
|
|
133
examples/mycelium/mycelium_basic.rhai
Normal file
133
examples/mycelium/mycelium_basic.rhai
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// Basic example of using the Mycelium client in Rhai
|
||||||
|
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:8989";
|
||||||
|
|
||||||
|
// Get node information
|
||||||
|
print("Getting node information:");
|
||||||
|
try {
|
||||||
|
let node_info = mycelium_get_node_info(api_url);
|
||||||
|
print(`Node subnet: ${node_info.nodeSubnet}`);
|
||||||
|
print(`Node public key: ${node_info.nodePubkey}`);
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error getting node info: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all peers
|
||||||
|
print("\nListing all peers:");
|
||||||
|
try {
|
||||||
|
let peers = mycelium_list_peers(api_url);
|
||||||
|
|
||||||
|
if peers.is_empty() {
|
||||||
|
print("No peers connected.");
|
||||||
|
} else {
|
||||||
|
for peer in peers {
|
||||||
|
print(`Peer Endpoint: ${peer.endpoint.proto}://${peer.endpoint.socketAddr}`);
|
||||||
|
print(` Type: ${peer.type}`);
|
||||||
|
print(` Connection State: ${peer.connectionState}`);
|
||||||
|
print(` Bytes sent: ${peer.txBytes}`);
|
||||||
|
print(` Bytes received: ${peer.rxBytes}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error listing peers: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new peer
|
||||||
|
print("\nAdding a new peer:");
|
||||||
|
let new_peer_address = "tcp://65.21.231.58:9651";
|
||||||
|
try {
|
||||||
|
let result = mycelium_add_peer(api_url, new_peer_address);
|
||||||
|
print(`Peer added: ${result.success}`);
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error adding peer: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List selected routes
|
||||||
|
print("\nListing selected routes:");
|
||||||
|
try {
|
||||||
|
let routes = mycelium_list_selected_routes(api_url);
|
||||||
|
|
||||||
|
if routes.is_empty() {
|
||||||
|
print("No selected routes.");
|
||||||
|
} else {
|
||||||
|
for route in routes {
|
||||||
|
print(`Subnet: ${route.subnet}`);
|
||||||
|
print(` Next hop: ${route.nextHop}`);
|
||||||
|
print(` Metric: ${route.metric}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error listing routes: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List fallback routes
|
||||||
|
print("\nListing fallback routes:");
|
||||||
|
try {
|
||||||
|
let routes = mycelium_list_fallback_routes(api_url);
|
||||||
|
|
||||||
|
if routes.is_empty() {
|
||||||
|
print("No fallback routes.");
|
||||||
|
} else {
|
||||||
|
for route in routes {
|
||||||
|
print(`Subnet: ${route.subnet}`);
|
||||||
|
print(` Next hop: ${route.nextHop}`);
|
||||||
|
print(` Metric: ${route.metric}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error listing fallback routes: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a message
|
||||||
|
// TO SEND A MESSAGE FILL IN THE DESTINATION IP ADDRESS
|
||||||
|
// -----------------------------------------------------//
|
||||||
|
// print("\nSending a message:");
|
||||||
|
// let destination = < FILL IN CORRECT DEST IP >
|
||||||
|
// let topic = "test";
|
||||||
|
// let message = "Hello from Rhai!";
|
||||||
|
// let deadline_secs = 60;
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// let result = mycelium_send_message(api_url, destination, topic, message, deadline_secs);
|
||||||
|
// print(`Message sent: ${result.success}`);
|
||||||
|
// if result.id {
|
||||||
|
// print(`Message ID: ${result.id}`);
|
||||||
|
// }
|
||||||
|
// } catch(err) {
|
||||||
|
// print(`Error sending message: ${err}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Receive messages
|
||||||
|
// RECEIVING MESSAGES SHOULD BE DONE ON THE DESTINATION NODE FROM THE CALL ABOVE
|
||||||
|
// -----------------------------------------------------------------------------//
|
||||||
|
// print("\nReceiving messages:");
|
||||||
|
// let receive_topic = "test";
|
||||||
|
// let count = 5;
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// let messages = mycelium_receive_messages(api_url, receive_topic, count);
|
||||||
|
|
||||||
|
// if messages.is_empty() {
|
||||||
|
// print("No messages received.");
|
||||||
|
// } else {
|
||||||
|
// for msg in messages {
|
||||||
|
// print(`Message from: ${msg.source}`);
|
||||||
|
// print(` Topic: ${msg.topic}`);
|
||||||
|
// print(` Content: ${msg.content}`);
|
||||||
|
// print(` Timestamp: ${msg.timestamp}`);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch(err) {
|
||||||
|
// print(`Error receiving messages: ${err}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Remove a peer
|
||||||
|
print("\nRemoving a peer:");
|
||||||
|
let peer_id = "tcp://65.21.231.58:9651"; // This is the peer we added earlier
|
||||||
|
try {
|
||||||
|
let result = mycelium_remove_peer(api_url, peer_id);
|
||||||
|
print(`Peer removed: ${result.success}`);
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error removing peer: ${err}`);
|
||||||
|
}
|
31
examples/mycelium/mycelium_receive_message.rhai
Normal file
31
examples/mycelium/mycelium_receive_message.rhai
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Script to receive Mycelium messages
|
||||||
|
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:2222";
|
||||||
|
|
||||||
|
// Receive messages
|
||||||
|
// This script will listen for messages on a specific topic.
|
||||||
|
// Ensure the sender script is using the same topic.
|
||||||
|
// -----------------------------------------------------------------------------//
|
||||||
|
print("\nReceiving messages:");
|
||||||
|
let receive_topic = "test_topic";
|
||||||
|
let wait_deadline_secs = 100;
|
||||||
|
|
||||||
|
print(`Listening for messages on topic '${receive_topic}'...`);
|
||||||
|
try {
|
||||||
|
let messages = mycelium_receive_messages(api_url, receive_topic, wait_deadline_secs);
|
||||||
|
|
||||||
|
if messages.is_empty() {
|
||||||
|
// print("No new messages received in this poll.");
|
||||||
|
} else {
|
||||||
|
print("Received a message:");
|
||||||
|
print(` Message id: ${messages.id}`);
|
||||||
|
print(` Message from: ${messages.srcIp}`);
|
||||||
|
print(` Topic: ${messages.topic}`);
|
||||||
|
print(` Payload: ${messages.payload}`);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error receiving messages: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Finished attempting to receive messages.");
|
25
examples/mycelium/mycelium_send_message.rhai
Normal file
25
examples/mycelium/mycelium_send_message.rhai
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Script to send a Mycelium message
|
||||||
|
|
||||||
|
// API URL for Mycelium
|
||||||
|
let api_url = "http://localhost:1111";
|
||||||
|
|
||||||
|
// Send a message
|
||||||
|
// TO SEND A MESSAGE FILL IN THE DESTINATION IP ADDRESS
|
||||||
|
// -----------------------------------------------------//
|
||||||
|
print("\nSending a message:");
|
||||||
|
let destination = "50e:6d75:4568:366e:f75:2ac3:bbb1:3fdd"; // IMPORTANT: Replace with the actual destination IP address
|
||||||
|
let topic = "test_topic";
|
||||||
|
let message = "Hello from Rhai sender!";
|
||||||
|
let deadline_secs = -10; // Seconds we wait for a reply
|
||||||
|
|
||||||
|
try {
|
||||||
|
print(`Attempting to send message to ${destination} on topic '${topic}'`);
|
||||||
|
let result = mycelium_send_message(api_url, destination, topic, message, deadline_secs);
|
||||||
|
print(`result: ${result}`);
|
||||||
|
print(`Message sent: ${result.success}`);
|
||||||
|
if result.id != "" {
|
||||||
|
print(`Message ID: ${result.id}`);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
print(`Error sending message: ${err}`);
|
||||||
|
}
|
@ -48,6 +48,7 @@ pub mod text;
|
|||||||
pub mod virt;
|
pub mod virt;
|
||||||
pub mod vault;
|
pub mod vault;
|
||||||
pub mod zinit_client;
|
pub mod zinit_client;
|
||||||
|
pub mod mycelium;
|
||||||
|
|
||||||
// Version information
|
// Version information
|
||||||
/// Returns the version of the SAL library
|
/// Returns the version of the SAL library
|
||||||
|
313
src/mycelium/mod.rs
Normal file
313
src/mycelium/mod.rs
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
use base64::{
|
||||||
|
engine::general_purpose,
|
||||||
|
Engine as _,
|
||||||
|
};
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// Get information about the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The node information as a JSON value, or an error message
|
||||||
|
pub async fn get_node_info(api_url: &str) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/v1/admin", api_url);
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get(&url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all peers connected to the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The list of peers as a JSON value, or an error message
|
||||||
|
pub async fn list_peers(api_url: &str) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/v1/admin/peers", api_url);
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get(&url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new peer to the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
/// * `peer_address` - The address of the peer to add
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The result of the operation as a JSON value, or an error message
|
||||||
|
pub async fn add_peer(api_url: &str, peer_address: &str) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/v1/admin/peers", api_url);
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.post(&url)
|
||||||
|
.json(&serde_json::json!({
|
||||||
|
"endpoint": peer_address
|
||||||
|
}))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if status == reqwest::StatusCode::NO_CONTENT {
|
||||||
|
// Successfully added, but no content to parse
|
||||||
|
return Ok(serde_json::json!({"success": true}));
|
||||||
|
}
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other success statuses that might have a body
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a peer from the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
/// * `peer_id` - The ID of the peer to remove
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The result of the operation as a JSON value, or an error message
|
||||||
|
pub async fn remove_peer(api_url: &str, peer_id: &str) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let peer_id_url_encoded = urlencoding::encode(peer_id);
|
||||||
|
let url = format!("{}/api/v1/admin/peers/{}", api_url, peer_id_url_encoded);
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.delete(&url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if status == reqwest::StatusCode::NO_CONTENT {
|
||||||
|
// Successfully removed, but no content to parse
|
||||||
|
return Ok(serde_json::json!({"success": true}));
|
||||||
|
}
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all selected routes in the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The list of selected routes as a JSON value, or an error message
|
||||||
|
pub async fn list_selected_routes(api_url: &str) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/v1/admin/routes/selected", api_url);
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get(&url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all fallback routes in the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The list of fallback routes as a JSON value, or an error message
|
||||||
|
pub async fn list_fallback_routes(api_url: &str) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/v1/admin/routes/fallback", api_url);
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get(&url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a message to a destination via the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
/// * `destination` - The destination address
|
||||||
|
/// * `topic` - The message topic
|
||||||
|
/// * `message` - The message content
|
||||||
|
/// * `reply_deadline` - The deadline in seconds; pass `-1` to indicate we do not want to wait on a reply
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The result of the operation as a JSON value, or an error message
|
||||||
|
pub async fn send_message(
|
||||||
|
api_url: &str,
|
||||||
|
destination: &str,
|
||||||
|
topic: &str,
|
||||||
|
message: &str,
|
||||||
|
reply_deadline: Option<Duration>, // This is passed in URL query
|
||||||
|
) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/v1/messages", api_url);
|
||||||
|
|
||||||
|
let mut request = client.post(&url);
|
||||||
|
if let Some(deadline) = reply_deadline {
|
||||||
|
request = request.query(&[("reply_timeout", deadline.as_secs())]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = request
|
||||||
|
.json(&serde_json::json!({
|
||||||
|
"dst": { "ip": destination },
|
||||||
|
"topic": general_purpose::STANDARD.encode(topic),
|
||||||
|
"payload": general_purpose::STANDARD.encode(message)
|
||||||
|
}))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive messages from a topic via the Mycelium node
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_url` - The URL of the Mycelium API
|
||||||
|
/// * `topic` - The message topic
|
||||||
|
/// * `wait_deadline` - Time we wait for receiving a message
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Value, String>` - The received messages as a JSON value, or an error message
|
||||||
|
pub async fn receive_messages(
|
||||||
|
api_url: &str,
|
||||||
|
topic: &str,
|
||||||
|
wait_deadline: Option<Duration>,
|
||||||
|
) -> Result<Value, String> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/v1/messages", api_url);
|
||||||
|
|
||||||
|
let mut request = client.get(&url);
|
||||||
|
|
||||||
|
if let Some(deadline) = wait_deadline {
|
||||||
|
request = request.query(&[
|
||||||
|
("topic", general_purpose::STANDARD.encode(topic)),
|
||||||
|
("timeout", deadline.as_secs().to_string()),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
request = request.query(&[("topic", general_purpose::STANDARD.encode(topic))])
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = request
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(format!("Request failed with status: {}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Value = response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to parse response: {}", e))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
@ -15,6 +15,7 @@ mod rfs;
|
|||||||
mod vault;
|
mod vault;
|
||||||
mod text;
|
mod text;
|
||||||
mod zinit;
|
mod zinit;
|
||||||
|
mod mycelium;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@ -95,6 +96,9 @@ pub use git::register_git_module;
|
|||||||
// Re-export zinit module
|
// Re-export zinit module
|
||||||
pub use zinit::register_zinit_module;
|
pub use zinit::register_zinit_module;
|
||||||
|
|
||||||
|
// Re-export mycelium module
|
||||||
|
pub use mycelium::register_mycelium_module;
|
||||||
|
|
||||||
// Re-export text module
|
// Re-export text module
|
||||||
pub use text::register_text_module;
|
pub use text::register_text_module;
|
||||||
// Re-export text functions directly from text module
|
// Re-export text functions directly from text module
|
||||||
@ -155,6 +159,9 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
|
|||||||
// Register Zinit module functions
|
// Register Zinit module functions
|
||||||
zinit::register_zinit_module(engine)?;
|
zinit::register_zinit_module(engine)?;
|
||||||
|
|
||||||
|
// Register Mycelium module functions
|
||||||
|
mycelium::register_mycelium_module(engine)?;
|
||||||
|
|
||||||
// Register Text module functions
|
// Register Text module functions
|
||||||
text::register_text_module(engine)?;
|
text::register_text_module(engine)?;
|
||||||
|
|
||||||
|
224
src/rhai/mycelium.rs
Normal file
224
src/rhai/mycelium.rs
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
//! Rhai wrappers for Mycelium client module functions
|
||||||
|
//!
|
||||||
|
//! This module provides Rhai wrappers for the functions in the Mycelium client module.
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map};
|
||||||
|
use crate::mycelium as client;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
use serde_json::Value;
|
||||||
|
use crate::rhai::error::ToRhaiError;
|
||||||
|
|
||||||
|
/// Register Mycelium module functions with the Rhai engine
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `engine` - The Rhai engine to register the functions with
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
|
||||||
|
pub fn register_mycelium_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
// Register Mycelium client functions
|
||||||
|
engine.register_fn("mycelium_get_node_info", mycelium_get_node_info);
|
||||||
|
engine.register_fn("mycelium_list_peers", mycelium_list_peers);
|
||||||
|
engine.register_fn("mycelium_add_peer", mycelium_add_peer);
|
||||||
|
engine.register_fn("mycelium_remove_peer", mycelium_remove_peer);
|
||||||
|
engine.register_fn("mycelium_list_selected_routes", mycelium_list_selected_routes);
|
||||||
|
engine.register_fn("mycelium_list_fallback_routes", mycelium_list_fallback_routes);
|
||||||
|
engine.register_fn("mycelium_send_message", mycelium_send_message);
|
||||||
|
engine.register_fn("mycelium_receive_messages", mycelium_receive_messages);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get a runtime
|
||||||
|
fn get_runtime() -> Result<Runtime, Box<EvalAltResult>> {
|
||||||
|
tokio::runtime::Runtime::new().map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Failed to create Tokio runtime: {}", e).into(),
|
||||||
|
rhai::Position::NONE
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to convert serde_json::Value to rhai::Dynamic
|
||||||
|
fn value_to_dynamic(value: Value) -> Dynamic {
|
||||||
|
match value {
|
||||||
|
Value::Null => Dynamic::UNIT,
|
||||||
|
Value::Bool(b) => Dynamic::from(b),
|
||||||
|
Value::Number(n) => {
|
||||||
|
if let Some(i) = n.as_i64() {
|
||||||
|
Dynamic::from(i)
|
||||||
|
} else if let Some(f) = n.as_f64() {
|
||||||
|
Dynamic::from(f)
|
||||||
|
} else {
|
||||||
|
Dynamic::from(n.to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Value::String(s) => Dynamic::from(s),
|
||||||
|
Value::Array(arr) => {
|
||||||
|
let mut rhai_arr = Array::new();
|
||||||
|
for item in arr {
|
||||||
|
rhai_arr.push(value_to_dynamic(item));
|
||||||
|
}
|
||||||
|
Dynamic::from(rhai_arr)
|
||||||
|
},
|
||||||
|
Value::Object(map) => {
|
||||||
|
let mut rhai_map = Map::new();
|
||||||
|
for (k, v) in map {
|
||||||
|
rhai_map.insert(k.into(), value_to_dynamic(v));
|
||||||
|
}
|
||||||
|
Dynamic::from_map(rhai_map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper trait to convert String errors to Rhai errors
|
||||||
|
impl<T> ToRhaiError<T> for Result<T, String> {
|
||||||
|
fn to_rhai_error(self) -> Result<T, Box<EvalAltResult>> {
|
||||||
|
self.map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Mycelium error: {}", e).into(),
|
||||||
|
rhai::Position::NONE
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mycelium Client Function Wrappers
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::get_node_info
|
||||||
|
///
|
||||||
|
/// Gets information about the Mycelium node.
|
||||||
|
pub fn mycelium_get_node_info(api_url: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::get_node_info(api_url).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let node_info = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(node_info))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::list_peers
|
||||||
|
///
|
||||||
|
/// Lists all peers connected to the Mycelium node.
|
||||||
|
pub fn mycelium_list_peers(api_url: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::list_peers(api_url).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let peers = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(peers))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::add_peer
|
||||||
|
///
|
||||||
|
/// Adds a new peer to the Mycelium node.
|
||||||
|
pub fn mycelium_add_peer(api_url: &str, peer_address: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::add_peer(api_url, peer_address).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::remove_peer
|
||||||
|
///
|
||||||
|
/// Removes a peer from the Mycelium node.
|
||||||
|
pub fn mycelium_remove_peer(api_url: &str, peer_id: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::remove_peer(api_url, peer_id).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::list_selected_routes
|
||||||
|
///
|
||||||
|
/// Lists all selected routes in the Mycelium node.
|
||||||
|
pub fn mycelium_list_selected_routes(api_url: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::list_selected_routes(api_url).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let routes = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(routes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::list_fallback_routes
|
||||||
|
///
|
||||||
|
/// Lists all fallback routes in the Mycelium node.
|
||||||
|
pub fn mycelium_list_fallback_routes(api_url: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::list_fallback_routes(api_url).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let routes = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(routes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::send_message
|
||||||
|
///
|
||||||
|
/// Sends a message to a destination via the Mycelium node.
|
||||||
|
pub fn mycelium_send_message(api_url: &str, destination: &str, topic: &str, message: &str, reply_deadline_secs: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let deadline = if reply_deadline_secs < 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Duration::from_secs(reply_deadline_secs as u64))
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::send_message(api_url, destination, topic, message, deadline).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for mycelium::receive_messages
|
||||||
|
///
|
||||||
|
/// Receives messages from a topic via the Mycelium node.
|
||||||
|
pub fn mycelium_receive_messages(api_url: &str, topic: &str, wait_deadline_secs: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let rt = get_runtime()?;
|
||||||
|
|
||||||
|
let deadline = if wait_deadline_secs < 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Duration::from_secs(wait_deadline_secs as u64))
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = rt.block_on(async {
|
||||||
|
client::receive_messages(api_url, topic, deadline).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let messages = result.to_rhai_error()?;
|
||||||
|
|
||||||
|
Ok(value_to_dynamic(messages))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user