mecha10-runtime 0.6.2

Runtime supervisor for Mecha10 nodes - launching, monitoring, and lifecycle management
Documentation
//! Launcher service integration for dynamic node launching

use crate::node::NodeRunner;
use crate::supervisor::{NodeHandle, Supervisor};
use anyhow::Result;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

/// Registry for node types that can be dynamically launched
pub type NodeFactory = Box<dyn Fn() -> Box<dyn NodeRunner> + Send + Sync>;

/// Launcher service for dynamic node management
pub struct Launcher {
    supervisor: Arc<Supervisor>,
    factories: Arc<RwLock<HashMap<String, NodeFactory>>>,
    running_nodes: Arc<RwLock<HashMap<String, NodeHandle>>>,
}

impl Launcher {
    /// Create a new launcher
    pub fn new(supervisor: Arc<Supervisor>) -> Self {
        Self {
            supervisor,
            factories: Arc::new(RwLock::new(HashMap::new())),
            running_nodes: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Register a node type with a factory function
    pub async fn register<F>(&self, node_type: String, factory: F)
    where
        F: Fn() -> Box<dyn NodeRunner> + Send + Sync + 'static,
    {
        let mut factories = self.factories.write().await;
        factories.insert(node_type, Box::new(factory));
    }

    /// Launch a node by type name
    pub async fn launch(&self, node_type: &str) -> Result<String> {
        let factories = self.factories.read().await;
        let factory = factories
            .get(node_type)
            .ok_or_else(|| anyhow::anyhow!("Unknown node type: {}", node_type))?;

        let node = factory();
        let node_name = node.name().to_string();

        // Check if already running
        let running = self.running_nodes.read().await;
        if running.contains_key(&node_name) {
            anyhow::bail!("Node already running: {}", node_name);
        }
        drop(running);

        // Launch the node
        let handle = self.supervisor.launch_node(node);
        let name = handle.name().to_string();

        let mut running = self.running_nodes.write().await;
        running.insert(name.clone(), handle);

        Ok(name)
    }

    /// Stop a running node
    pub async fn stop(&self, node_name: &str) -> Result<()> {
        let mut running = self.running_nodes.write().await;
        let handle = running
            .remove(node_name)
            .ok_or_else(|| anyhow::anyhow!("Node not running: {}", node_name))?;

        handle.abort();
        Ok(())
    }

    /// List all running nodes
    pub async fn list_running(&self) -> Vec<String> {
        let running = self.running_nodes.read().await;
        running.keys().cloned().collect()
    }

    /// Check if a node is running
    pub async fn is_running(&self, node_name: &str) -> bool {
        let running = self.running_nodes.read().await;
        running.contains_key(node_name)
    }

    /// Get list of registered node types
    pub async fn list_types(&self) -> Vec<String> {
        let factories = self.factories.read().await;
        factories.keys().cloned().collect()
    }
}