greentic-dev 0.6.0-dev.25162253331

Developer CLI and local tooling for Greentic flows, packs, and components
Documentation
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum WizardFrontend {
    Text,
    Json,
    AdaptiveCard,
}

impl WizardFrontend {
    pub fn parse(value: &str) -> Option<Self> {
        match value {
            "text" => Some(Self::Text),
            "json" => Some(Self::Json),
            "adaptive-card" => Some(Self::AdaptiveCard),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WizardPlan {
    pub plan_version: u32,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<String>,
    pub metadata: WizardPlanMetadata,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub inputs: BTreeMap<String, String>,
    pub steps: Vec<WizardStep>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WizardPlanMetadata {
    pub target: String,
    pub mode: String,
    pub locale: String,
    pub frontend: WizardFrontend,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type")]
pub enum WizardStep {
    LaunchPackWizard,
    LaunchBundleWizard,
    RunCommand(RunCommandStep),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RunCommandStep {
    pub program: String,
    pub args: Vec<String>,
    #[serde(default, skip_serializing_if = "is_false")]
    pub destructive: bool,
}

fn is_false(v: &bool) -> bool {
    !*v
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct WizardAnswers {
    pub data: serde_json::Value,
}

impl Default for WizardAnswers {
    fn default() -> Self {
        Self {
            data: serde_json::Value::Object(Default::default()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{RunCommandStep, WizardAnswers, WizardFrontend, WizardStep};

    #[test]
    fn frontend_parser_accepts_supported_values() {
        assert_eq!(WizardFrontend::parse("text"), Some(WizardFrontend::Text));
        assert_eq!(WizardFrontend::parse("json"), Some(WizardFrontend::Json));
        assert_eq!(
            WizardFrontend::parse("adaptive-card"),
            Some(WizardFrontend::AdaptiveCard)
        );
        assert_eq!(WizardFrontend::parse("html"), None);
    }

    #[test]
    fn default_answers_start_with_empty_object() {
        let answers = WizardAnswers::default();
        assert!(answers.data.is_object());
        assert_eq!(answers.data.as_object().unwrap().len(), 0);
    }

    #[test]
    fn run_command_step_omits_false_destructive_flag() {
        let step = WizardStep::RunCommand(RunCommandStep {
            program: "echo".to_string(),
            args: vec!["hello".to_string()],
            destructive: false,
        });

        let value = serde_json::to_value(&step).unwrap();
        assert!(value.get("destructive").is_none());
    }
}