greentic-operator 0.4.43

Greentic operator CLI for local dev and demo orchestration.
Documentation
use std::path::{Path, PathBuf};

use anyhow::{Context, Result, anyhow};

use crate::domains;

#[derive(Debug, Clone)]
pub struct DemoPack {
    pub pack_id: String,
    pub pack_path: PathBuf,
    pub entry_flows: Vec<String>,
    pub default_flow_source: EntryFlowSource,
}

#[derive(Clone, Copy, Debug)]
pub enum EntryFlowSource {
    MetaEntryFlows,
    FlowsList,
}

impl EntryFlowSource {
    fn description(self) -> &'static str {
        match self {
            EntryFlowSource::MetaEntryFlows => "meta.entry_flows",
            EntryFlowSource::FlowsList => "manifest.flows[*].id/entrypoints",
        }
    }
}

impl DemoPack {
    pub fn select_flow(&self, requested: Option<&str>) -> Result<String> {
        if self.entry_flows.is_empty() {
            return Err(anyhow!(
                "default flow not declared ({}) and no flows available; available flows: []",
                self.default_flow_source.description()
            ));
        }
        if let Some(flow) = requested {
            if self.entry_flows.iter().any(|candidate| candidate == flow) {
                return Ok(flow.to_string());
            }
            return Err(anyhow!(
                "flow `{flow}` not declared via {}; available flows: {}",
                self.default_flow_source.description(),
                self.entry_flows.join(", ")
            ));
        }
        Ok(self.entry_flows[0].clone())
    }
}

pub fn resolve_pack(packs_dir: &Path, pack_name: &str) -> Result<DemoPack> {
    let pack_path = packs_dir.join(pack_name);
    if !pack_path.exists() {
        return Err(anyhow!(
            "pack {pack_name} not found under {}",
            packs_dir.display()
        ));
    }
    let meta = domains::read_pack_meta(&pack_path)
        .with_context(|| format!("failed to read manifest for pack {}", pack_path.display()))?;
    Ok(DemoPack {
        pack_id: meta.pack_id.clone(),
        pack_path,
        entry_flows: meta.entry_flows.clone(),
        default_flow_source: EntryFlowSource::MetaEntryFlows,
    })
}