sal-mycelium 0.1.0

SAL Mycelium - Client interface for interacting with Mycelium node's HTTP API
Documentation
//! 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 crate as client;
use rhai::Position;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
use serde_json::Value;
use tokio::runtime::Runtime;

/// 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)
        }
    }
}

//
// 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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    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.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Mycelium error: {}", e).into(),
            Position::NONE,
        ))
    })?;

    Ok(value_to_dynamic(messages))
}