mecha10_cli/dev/
lifecycle_adapter.rs1use crate::types::project::{NodeRef, ProjectConfig, TargetsConfig};
13use anyhow::Result;
14use mecha10_runtime::lifecycle::diff_nodes;
15use std::collections::{HashMap, HashSet};
16
17pub struct CliLifecycleManager {
18 current_mode: String,
19
20 running_nodes: HashSet<String>,
21
22 mode_config: HashMap<String, Vec<String>>,
23}
24
25#[derive(Debug)]
26#[allow(dead_code)]
27pub struct ModeTransitionDiff {
28 pub start: Vec<String>,
29
30 pub stop: Vec<String>,
31}
32
33impl CliLifecycleManager {
34 pub fn from_project_config(config: &ProjectConfig) -> Option<Self> {
42 let mode_config: HashMap<String, Vec<String>> = config
50 .targets
51 .present_entries()
52 .into_iter()
53 .map(|(name, nodes)| (name.to_string(), nodes.iter().map(NodeRef::wire_identifier).collect()))
54 .collect();
55
56 if mode_config.is_empty() {
57 return None;
58 }
59
60 Some(Self {
61 current_mode: "dev".to_string(),
62 running_nodes: HashSet::new(),
63 mode_config,
64 })
65 }
66
67 pub fn current_mode(&self) -> &str {
69 &self.current_mode
70 }
71
72 pub fn has_mode(&self, mode: &str) -> bool {
74 self.mode_config.contains_key(mode)
75 }
76
77 pub fn nodes_for_current_mode(&self) -> Vec<String> {
79 self.mode_config.get(&self.current_mode).cloned().unwrap_or_default()
80 }
81
82 #[allow(dead_code)]
83 pub fn calculate_mode_diff(&self, target_mode: &str) -> Result<ModeTransitionDiff> {
84 let target_nodes = self
85 .mode_config
86 .get(target_mode)
87 .ok_or_else(|| anyhow::anyhow!("Mode '{}' not found", target_mode))?;
88
89 let (start, stop) = diff_nodes(target_nodes, &self.running_nodes);
90
91 Ok(ModeTransitionDiff { start, stop })
92 }
93
94 pub fn mark_nodes_running(&mut self, nodes: &[String]) {
96 for node in nodes {
97 self.running_nodes.insert(node.clone());
98 }
99 }
100
101 #[allow(dead_code)]
103 pub fn mark_nodes_stopped(&mut self, nodes: &[String]) {
104 for node in nodes {
105 self.running_nodes.remove(node);
106 }
107 }
108
109 #[allow(dead_code)]
114 pub fn change_mode(&mut self, target_mode: &str) -> Result<ModeTransitionDiff> {
115 let diff = self.calculate_mode_diff(target_mode)?;
116 self.current_mode = target_mode.to_string();
117 Ok(diff)
118 }
119
120 #[allow(dead_code)]
122 pub fn available_modes(&self) -> Vec<&str> {
123 self.mode_config.keys().map(|s| s.as_str()).collect()
124 }
125
126 #[allow(dead_code)]
131 pub fn validate(targets: &TargetsConfig, available_nodes: &[String]) -> Result<()> {
132 let node_set: HashSet<_> = available_nodes.iter().map(|s| s.as_str()).collect();
133
134 for (mode_name, nodes) in targets.present_entries() {
135 for node in nodes {
136 if !node_set.contains(node.name()) {
137 return Err(anyhow::anyhow!(
138 "Mode '{}' references unknown node '{}'",
139 mode_name,
140 node.name()
141 ));
142 }
143 }
144 }
145
146 Ok(())
147 }
148}