Skip to main content

greentic_dev/wizard/
plan.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6#[serde(rename_all = "kebab-case")]
7pub enum WizardFrontend {
8    Text,
9    Json,
10    AdaptiveCard,
11}
12
13impl WizardFrontend {
14    pub fn parse(value: &str) -> Option<Self> {
15        match value {
16            "text" => Some(Self::Text),
17            "json" => Some(Self::Json),
18            "adaptive-card" => Some(Self::AdaptiveCard),
19            _ => None,
20        }
21    }
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
25pub struct WizardPlan {
26    pub plan_version: u32,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub created_at: Option<String>,
29    pub metadata: WizardPlanMetadata,
30    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
31    pub inputs: BTreeMap<String, String>,
32    pub steps: Vec<WizardStep>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
36pub struct WizardPlanMetadata {
37    pub target: String,
38    pub mode: String,
39    pub locale: String,
40    pub frontend: WizardFrontend,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
44#[serde(tag = "type")]
45pub enum WizardStep {
46    LaunchPackWizard,
47    LaunchBundleWizard,
48    RunCommand(RunCommandStep),
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
52pub struct RunCommandStep {
53    pub program: String,
54    pub args: Vec<String>,
55    #[serde(default, skip_serializing_if = "is_false")]
56    pub destructive: bool,
57}
58
59fn is_false(v: &bool) -> bool {
60    !*v
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
64pub struct WizardAnswers {
65    pub data: serde_json::Value,
66}
67
68impl Default for WizardAnswers {
69    fn default() -> Self {
70        Self {
71            data: serde_json::Value::Object(Default::default()),
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::{RunCommandStep, WizardAnswers, WizardFrontend, WizardStep};
79
80    #[test]
81    fn frontend_parser_accepts_supported_values() {
82        assert_eq!(WizardFrontend::parse("text"), Some(WizardFrontend::Text));
83        assert_eq!(WizardFrontend::parse("json"), Some(WizardFrontend::Json));
84        assert_eq!(
85            WizardFrontend::parse("adaptive-card"),
86            Some(WizardFrontend::AdaptiveCard)
87        );
88        assert_eq!(WizardFrontend::parse("html"), None);
89    }
90
91    #[test]
92    fn default_answers_start_with_empty_object() {
93        let answers = WizardAnswers::default();
94        assert!(answers.data.is_object());
95        assert_eq!(answers.data.as_object().unwrap().len(), 0);
96    }
97
98    #[test]
99    fn run_command_step_omits_false_destructive_flag() {
100        let step = WizardStep::RunCommand(RunCommandStep {
101            program: "echo".to_string(),
102            args: vec!["hello".to_string()],
103            destructive: false,
104        });
105
106        let value = serde_json::to_value(&step).unwrap();
107        assert!(value.get("destructive").is_none());
108    }
109}