mecha10_cli/dev/
lifecycle_adapter.rs1use crate::types::project::{NodeRef, ProjectConfig, TargetsConfig};
6use anyhow::Result;
7use mecha10_runtime::lifecycle::diff_nodes;
8use std::collections::{HashMap, HashSet};
9
10pub struct CliLifecycleManager {
11 current_mode: String,
12
13 running_nodes: HashSet<String>,
14
15 mode_config: HashMap<String, Vec<String>>,
16}
17
18#[derive(Debug)]
19#[allow(dead_code)]
20pub struct ModeTransitionDiff {
21 pub start: Vec<String>,
22
23 pub stop: Vec<String>,
24}
25
26impl CliLifecycleManager {
27 pub fn from_project_config(config: &ProjectConfig) -> Option<Self> {
31 let mode_config: HashMap<String, Vec<String>> = config
32 .targets
33 .present_entries()
34 .into_iter()
35 .map(|(name, nodes)| (name.to_string(), nodes.iter().map(NodeRef::wire_identifier).collect()))
36 .collect();
37
38 if mode_config.is_empty() {
39 return None;
40 }
41
42 Some(Self {
43 current_mode: "dev".to_string(),
44 running_nodes: HashSet::new(),
45 mode_config,
46 })
47 }
48
49 pub fn current_mode(&self) -> &str {
51 &self.current_mode
52 }
53
54 pub fn has_mode(&self, mode: &str) -> bool {
56 self.mode_config.contains_key(mode)
57 }
58
59 pub fn nodes_for_current_mode(&self) -> Vec<String> {
61 self.mode_config.get(&self.current_mode).cloned().unwrap_or_default()
62 }
63
64 #[allow(dead_code)]
65 pub fn calculate_mode_diff(&self, target_mode: &str) -> Result<ModeTransitionDiff> {
66 let target_nodes = self
67 .mode_config
68 .get(target_mode)
69 .ok_or_else(|| anyhow::anyhow!("Mode '{}' not found", target_mode))?;
70
71 let (start, stop) = diff_nodes(target_nodes, &self.running_nodes);
72
73 Ok(ModeTransitionDiff { start, stop })
74 }
75
76 pub fn mark_nodes_running(&mut self, nodes: &[String]) {
78 for node in nodes {
79 self.running_nodes.insert(node.clone());
80 }
81 }
82
83 #[allow(dead_code)]
85 pub fn mark_nodes_stopped(&mut self, nodes: &[String]) {
86 for node in nodes {
87 self.running_nodes.remove(node);
88 }
89 }
90
91 #[allow(dead_code)]
96 pub fn change_mode(&mut self, target_mode: &str) -> Result<ModeTransitionDiff> {
97 let diff = self.calculate_mode_diff(target_mode)?;
98 self.current_mode = target_mode.to_string();
99 Ok(diff)
100 }
101
102 #[allow(dead_code)]
104 pub fn available_modes(&self) -> Vec<&str> {
105 self.mode_config.keys().map(|s| s.as_str()).collect()
106 }
107
108 #[allow(dead_code)]
113 pub fn validate(targets: &TargetsConfig, available_nodes: &[String]) -> Result<()> {
114 let node_set: HashSet<_> = available_nodes.iter().map(|s| s.as_str()).collect();
115
116 for (mode_name, nodes) in targets.present_entries() {
117 for node in nodes {
118 if !node_set.contains(node.name()) {
119 return Err(anyhow::anyhow!(
120 "Mode '{}' references unknown node '{}'",
121 mode_name,
122 node.name()
123 ));
124 }
125 }
126 }
127
128 Ok(())
129 }
130}