mecha10_cli/dev/
node_selection.rs

1//! Node selection logic for development mode
2
3use crate::services::ProcessService;
4use crate::types::{NodeEntry, ProjectConfig};
5
6/// Information about a node to run
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct NodeToRun {
9    /// Node name
10    pub name: String,
11    /// Path to the node package (e.g., "mecha10-nodes-motor-fake" or "nodes/custom")
12    pub path: String,
13    /// Whether this is a monorepo node (starts with "mecha10-nodes-")
14    pub is_monorepo_node: bool,
15}
16
17impl NodeToRun {
18    /// Create a new NodeToRun from NodeEntry
19    pub fn from_config(node: &NodeEntry) -> Self {
20        let is_monorepo = node.path.starts_with("mecha10-nodes-");
21        Self {
22            name: node.name.clone(),
23            path: node.path.clone(),
24            is_monorepo_node: is_monorepo,
25        }
26    }
27
28    /// Get the binary path for this node
29    ///
30    /// Uses smart resolution to find the best binary location:
31    /// - Framework dev mode: local build in target/
32    /// - Production mode: globally installed binary (if available)
33    /// - Fallback: local build
34    ///
35    /// Note: This method is no longer used by CLI (replaced by node-runner in Phase 2).
36    /// Kept for testing purposes.
37    #[allow(dead_code)]
38    pub fn binary_path(&self, project_name: &str) -> String {
39        ProcessService::resolve_node_binary(&self.name, self.is_monorepo_node, project_name)
40    }
41
42    /// Get command args for running this node
43    ///
44    /// Returns args based on whether we're using:
45    /// - Global binary: no args (binary IS the node)
46    /// - Local project build: ["node", "node_name"] subcommand
47    ///
48    /// Note: This method is no longer used by CLI (replaced by node-runner in Phase 2).
49    /// Kept for testing purposes.
50    #[allow(dead_code)]
51    pub fn args(&self, project_name: &str) -> Vec<String> {
52        if self.is_monorepo_node {
53            // Check if we're using a global binary
54            let binary_path = self.binary_path(project_name);
55
56            // If the binary path doesn't start with "target/", it's a global binary
57            // Global binaries don't need the "node" subcommand
58            if !binary_path.starts_with("target/") {
59                vec![]
60            } else {
61                // Local build uses "node" subcommand
62                vec!["node".to_string(), self.name.clone()]
63            }
64        } else {
65            vec![]
66        }
67    }
68}
69
70/// Get list of nodes to run based on configuration
71///
72/// # Arguments
73///
74/// * `requested_nodes` - Specific nodes requested by user (empty = all enabled)
75/// * `project_config` - Project configuration
76///
77/// # Returns
78///
79/// List of nodes to run
80pub fn get_nodes_to_run(requested_nodes: &[String], project_config: &ProjectConfig) -> Vec<NodeToRun> {
81    // Collect all nodes from config
82    let mut all_nodes: Vec<NodeEntry> = Vec::new();
83    all_nodes.extend(project_config.nodes.drivers.clone());
84    all_nodes.extend(project_config.nodes.custom.clone());
85
86    // If specific nodes requested, filter to those
87    let nodes_to_run: Vec<NodeEntry> = if requested_nodes.is_empty() {
88        // Use all enabled nodes
89        all_nodes.into_iter().filter(|n| n.enabled).collect()
90    } else {
91        // Use only requested nodes (ignore enabled status)
92        all_nodes
93            .into_iter()
94            .filter(|n| requested_nodes.contains(&n.name))
95            .collect()
96    };
97
98    // Convert to NodeToRun
99    nodes_to_run.iter().map(NodeToRun::from_config).collect()
100}
101
102/// Get list of all available node names
103pub fn get_available_node_names(project_config: &ProjectConfig) -> Vec<String> {
104    let mut names = Vec::new();
105
106    for node in &project_config.nodes.drivers {
107        names.push(node.name.clone());
108    }
109    for node in &project_config.nodes.custom {
110        names.push(node.name.clone());
111    }
112
113    names
114}
115
116/// Get list of enabled node names
117#[allow(dead_code)] // Tested, planned for future use
118pub fn get_enabled_node_names(project_config: &ProjectConfig) -> Vec<String> {
119    let mut names = Vec::new();
120
121    for node in &project_config.nodes.drivers {
122        if node.enabled {
123            names.push(node.name.clone());
124        }
125    }
126    for node in &project_config.nodes.custom {
127        if node.enabled {
128            names.push(node.name.clone());
129        }
130    }
131
132    names
133}