//! Rhai wrappers for Mycelium client module functions //! //! This module provides Rhai wrappers for the functions in the Mycelium client module. 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>` - Ok if registration was successful, Err otherwise pub fn register_mycelium_module(engine: &mut Engine) -> Result<(), Box> { // 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> { 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 ToRhaiError for Result { fn to_rhai_error(self) -> Result> { 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> { 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> { 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> { 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> { 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> { 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> { 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, deadline_secs: i64) -> Result> { let rt = get_runtime()?; // Convert deadline to u64 let deadline = if deadline_secs < 0 { return Err(Box::new(EvalAltResult::ErrorRuntime( "Deadline cannot be negative".into(), rhai::Position::NONE ))); } else { 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, count: i64) -> Result> { let rt = get_runtime()?; // Convert count to u32 let count = if count < 0 { return Err(Box::new(EvalAltResult::ErrorRuntime( "Count cannot be negative".into(), rhai::Position::NONE ))); } else if count > u32::MAX as i64 { return Err(Box::new(EvalAltResult::ErrorRuntime( format!("Count too large, maximum is {}", u32::MAX).into(), rhai::Position::NONE ))); } else { count as u32 }; let result = rt.block_on(async { client::receive_messages(api_url, topic, count).await }); let messages = result.to_rhai_error()?; Ok(value_to_dynamic(messages)) }