use crate::types::project::{LifecycleConfig, ModeConfig, ProjectConfig};
use anyhow::Result;
use std::collections::{HashMap, HashSet};
pub struct CliLifecycleManager {
current_mode: String,
running_nodes: HashSet<String>,
mode_config: HashMap<String, ModeConfig>,
}
#[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 lifecycle = config.lifecycle.as_ref()?;
Some(Self {
current_mode: lifecycle.default_mode.clone(),
running_nodes: HashSet::new(),
mode_config: lifecycle.modes.clone(),
})
}
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)
.map(|config| config.nodes.clone())
.unwrap_or_default()
}
#[allow(dead_code)] pub fn calculate_mode_diff(&self, target_mode: &str) -> Result<ModeTransitionDiff> {
let target_config = self
.mode_config
.get(target_mode)
.ok_or_else(|| anyhow::anyhow!("Mode '{}' not found", target_mode))?;
let target_nodes: HashSet<_> = target_config.nodes.iter().map(|s| s.as_str()).collect();
let start: Vec<_> = target_nodes
.iter()
.filter(|n| !self.running_nodes.contains(**n))
.map(|s| s.to_string())
.collect();
let stop: Vec<_> = self
.running_nodes
.iter()
.filter(|n| !target_nodes.contains(n.as_str()))
.cloned()
.collect();
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(lifecycle: &LifecycleConfig, available_nodes: &[String]) -> Result<()> {
if !lifecycle.modes.contains_key(&lifecycle.default_mode) {
return Err(anyhow::anyhow!(
"Default mode '{}' not found in modes",
lifecycle.default_mode
));
}
let node_set: HashSet<_> = available_nodes.iter().map(|s| s.as_str()).collect();
for (mode_name, mode_config) in &lifecycle.modes {
for node in &mode_config.nodes {
if !node_set.contains(node.as_str()) {
return Err(anyhow::anyhow!(
"Mode '{}' references unknown node '{}'",
mode_name,
node
));
}
}
}
Ok(())
}
}