thenodes 0.3.0

TheNodes is a modular, plugin-driven P2P node framework for Rust, supporting node-embedded plugins (NEP) and core-as-a-library (CAL) modes with async-first APIs.
Documentation
use crate::custom_plugin_host::{unix_timestamp, CustomPluginHost};
use std::sync::Arc;
use thenodes::plugin_host::PluginContext;

/// Application-specific command handler for the prompt interface
pub struct AppCommands {
    host: Arc<CustomPluginHost>,
    context: PluginContext,
}

impl AppCommands {
    pub fn new(host: Arc<CustomPluginHost>, context: PluginContext) -> Self {
        Self { host, context }
    }

    /// Handle application commands
    pub async fn handle_command(
        &self,
        input: &str,
    ) -> Result<Option<String>, Box<dyn std::error::Error>> {
        let parts: Vec<&str> = input.split_whitespace().collect();
        if parts.is_empty() {
            return Ok(None);
        }

        let command = parts[0];
        let args = if parts.len() > 1 {
            parts[1..].to_vec()
        } else {
            vec![]
        };

        match command {
            // Application-specific commands
            "help" | "?" => Ok(Some(self.show_help())),
            "status" | "info" => {
                let result = self.host.handle_custom_command("status", args).await?;
                Ok(Some(result))
            }
            "sessions" => {
                let result = self.host.handle_custom_command("sessions", args).await?;
                Ok(Some(result))
            }
            "plugins" => {
                let result = self.host.handle_custom_command("plugins", args).await?;
                Ok(Some(result))
            }
            "data" => {
                let result = self.host.handle_custom_command("data", args).await?;
                Ok(Some(result))
            }
            "send" => self.handle_send_command(args).await,
            "session" => self.handle_session_command(args).await,

            // Let custom plugins handle unknown commands
            _ => match self.host.handle_custom_command(command, args).await {
                Ok(result) => Ok(Some(result)),
                Err(_) => Ok(Some(format!(
                    "❓ Unknown command: '{}'. Type 'help' for available commands.",
                    command
                ))),
            },
        }
    }

    fn show_help(&self) -> String {
        r#"
🎮 {{APP_NAME}} Commands:

📊 Information:
  help, ?          - Show this help message  
  status, info     - Show host status
  sessions         - List active sessions
  plugins          - List loaded custom plugins

📂 Data Management:
  data set <key> <value>  - Store custom data
  data get <key>          - Retrieve custom data  
  data list              - List all custom data

📤 Messaging:
  send <type> <target> <payload>  - Send custom message
  
🎯 Session Management:
  session create <id>     - Create new session
  session destroy <id>    - Destroy session
  session list           - List sessions (same as 'sessions')

🔌 Plugin Commands:
  (Custom plugin commands will appear here when plugins are loaded)

💡 Tips:
  - Commands are case-sensitive
  - Use Ctrl+C to exit
        "#
        .trim()
        .to_string()
    }

    async fn handle_send_command(
        &self,
        args: Vec<&str>,
    ) -> Result<Option<String>, Box<dyn std::error::Error>> {
        if args.len() < 3 {
            return Ok(Some(
                "Usage: send <message_type> <target> <payload_json>".to_string(),
            ));
        }

        let msg_type = args[0];
        let target = args[1];
        let payload_str = args[2..].join(" ");

        // Try to parse payload as JSON
        let payload: serde_json::Value = match serde_json::from_str(&payload_str) {
            Ok(json) => json,
            Err(_) => {
                // If not valid JSON, treat as simple string
                serde_json::Value::String(payload_str)
            }
        };

        self.host
            .send_custom_message(msg_type, payload, target, &self.context)
            .await?;
        Ok(Some(format!("📤 Sent {} message to {}", msg_type, target)))
    }

    async fn handle_session_command(
        &self,
        args: Vec<&str>,
    ) -> Result<Option<String>, Box<dyn std::error::Error>> {
        if args.is_empty() {
            return Ok(Some(
                "Usage: session [create <id> | destroy <id> | list]".to_string(),
            ));
        }

        match args[0] {
            "create" if args.len() >= 2 => {
                let session_id = args[1];
                let payload = serde_json::json!({
                    "session_id": session_id,
                    "timestamp": unix_timestamp()
                });

                self.host
                    .send_custom_message("session_create", payload, "*", &self.context)
                    .await?;
                Ok(Some(format!("🎯 Creating session: {}", session_id)))
            }
            "destroy" if args.len() >= 2 => {
                let session_id = args[1];
                let payload = serde_json::json!({
                    "session_id": session_id,
                    "timestamp": unix_timestamp()
                });

                self.host
                    .send_custom_message("session_destroy", payload, "*", &self.context)
                    .await?;
                Ok(Some(format!("🗑️ Destroying session: {}", session_id)))
            }
            "list" => {
                let result = self.host.handle_custom_command("sessions", vec![]).await?;
                Ok(Some(result))
            }
            _ => Ok(Some(
                "Usage: session [create <id> | destroy <id> | list]".to_string(),
            )),
        }
    }
}

// Integration with TheNodes prompt system
impl AppCommands {
    /// Handle prompt input - this integrates with TheNodes' prompt system
    pub async fn handle_prompt_input(
        &self,
        input: &str,
    ) -> Result<String, Box<dyn std::error::Error>> {
        match self.handle_command(input).await? {
            Some(response) => Ok(response),
            None => Ok("".to_string()),
        }
    }
}