greentic-operator 0.4.34

Greentic operator CLI for local dev and demo orchestration.
use crate::demo::card::CardView;
use serde_json::Value as JsonValue;
use std::collections::HashMap;

#[derive(Clone)]
pub struct Snapshot {
    pub output: JsonValue,
    pub card: Option<CardView>,
    pub pending_inputs: HashMap<String, String>,
}

impl Snapshot {
    pub fn new(
        output: JsonValue,
        card: Option<CardView>,
        pending_inputs: HashMap<String, String>,
    ) -> Self {
        Self {
            output,
            card,
            pending_inputs,
        }
    }
}

pub struct DemoHistory {
    stack: Vec<Snapshot>,
}

impl DemoHistory {
    pub fn new() -> Self {
        Self { stack: Vec::new() }
    }

    pub fn push(&mut self, snapshot: Snapshot) {
        self.stack.push(snapshot);
    }

    pub fn latest(&self) -> Option<&Snapshot> {
        self.stack.last()
    }

    pub fn go_back(&mut self) -> Option<&Snapshot> {
        if self.stack.len() <= 1 {
            return None;
        }
        self.stack.pop();
        self.stack.last()
    }
}

impl Default for DemoHistory {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    fn make_snapshot(version: &str, inputs: &[(&str, &str)]) -> Snapshot {
        let card = CardView {
            version: Some(version.to_string()),
            title: Some(format!("Card {version}")),
            summary_text: None,
            body_texts: Vec::new(),
            inputs: Vec::new(),
            actions: Vec::new(),
        };
        let pending = inputs
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();
        Snapshot::new(
            json!({ "card": { "version": version } }),
            Some(card),
            pending,
        )
    }

    #[test]
    fn go_back_restores_previous_snapshot() {
        let mut history = DemoHistory::new();
        history.push(make_snapshot("1.0", &[("foo", "bar")]));
        history.push(make_snapshot("2.0", &[("foo", "baz")]));
        let previous = history.go_back().expect("should be able to go back");
        assert_eq!(
            previous.card.as_ref().unwrap().version.as_deref(),
            Some("1.0")
        );
        assert_eq!(
            previous.pending_inputs.get("foo").map(String::as_str),
            Some("bar")
        );
    }

    #[test]
    fn cannot_go_back_past_first_snapshot() {
        let mut history = DemoHistory::new();
        history.push(make_snapshot("1.0", &[("foo", "bar")]));
        assert!(history.go_back().is_none());
    }
}