use crate::types::project::{NodeRef, ProjectConfig, TargetsConfig};
use anyhow::Result;
use mecha10_runtime::lifecycle::diff_nodes;
use std::collections::{HashMap, HashSet};
pub struct CliLifecycleManager {
current_mode: String,
running_nodes: HashSet<String>,
mode_config: HashMap<String, Vec<String>>,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct ModeTransitionDiff {
pub start: Vec<String>,
pub stop: Vec<String>,
}
impl CliLifecycleManager {
pub fn from_project_config(config: &ProjectConfig) -> Option<Self> {
let mode_config: HashMap<String, Vec<String>> = config
.targets
.present_entries()
.into_iter()
.map(|(name, nodes)| (name.to_string(), nodes.iter().map(NodeRef::wire_identifier).collect()))
.collect();
if mode_config.is_empty() {
return None;
}
Some(Self {
current_mode: "dev".to_string(),
running_nodes: HashSet::new(),
mode_config,
})
}
pub fn current_mode(&self) -> &str {
&self.current_mode
}
pub fn has_mode(&self, mode: &str) -> bool {
self.mode_config.contains_key(mode)
}
pub fn nodes_for_current_mode(&self) -> Vec<String> {
self.mode_config.get(&self.current_mode).cloned().unwrap_or_default()
}
#[allow(dead_code)]
pub fn calculate_mode_diff(&self, target_mode: &str) -> Result<ModeTransitionDiff> {
let target_nodes = self
.mode_config
.get(target_mode)
.ok_or_else(|| anyhow::anyhow!("Mode '{}' not found", target_mode))?;
let (start, stop) = diff_nodes(target_nodes, &self.running_nodes);
Ok(ModeTransitionDiff { start, stop })
}
pub fn mark_nodes_running(&mut self, nodes: &[String]) {
for node in nodes {
self.running_nodes.insert(node.clone());
}
}
#[allow(dead_code)]
pub fn mark_nodes_stopped(&mut self, nodes: &[String]) {
for node in nodes {
self.running_nodes.remove(node);
}
}
#[allow(dead_code)]
pub fn change_mode(&mut self, target_mode: &str) -> Result<ModeTransitionDiff> {
let diff = self.calculate_mode_diff(target_mode)?;
self.current_mode = target_mode.to_string();
Ok(diff)
}
#[allow(dead_code)]
pub fn available_modes(&self) -> Vec<&str> {
self.mode_config.keys().map(|s| s.as_str()).collect()
}
#[allow(dead_code)]
pub fn validate(targets: &TargetsConfig, available_nodes: &[String]) -> Result<()> {
let node_set: HashSet<_> = available_nodes.iter().map(|s| s.as_str()).collect();
for (mode_name, nodes) in targets.present_entries() {
for node in nodes {
if !node_set.contains(node.name()) {
return Err(anyhow::anyhow!(
"Mode '{}' references unknown node '{}'",
mode_name,
node.name()
));
}
}
}
Ok(())
}
}