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}