1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use crate::PicoValue;

use serde::Serialize;
use serde_json::json;
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize)]
pub enum StateValue {
    Boolean(bool),
    Number(isize),
    String(String),
}

type Namespace = String;
pub type VariablesMap = HashMap<String, PicoValue>;
type NamespaceVariableMap = HashMap<Namespace, VariablesMap>;

#[derive(Serialize, Debug)]
pub struct PicoContext {
    pub namespaced_variables: NamespaceVariableMap,
    pub variables: VariablesMap,
    pub local_variables: VariablesMap,
    pub input_json: Option<serde_json::Value>,
}

impl Default for PicoContext {
    fn default() -> Self {
        Self {
            namespaced_variables: HashMap::new(),
            variables: HashMap::new(),
            local_variables: HashMap::new(),
            input_json: None,
        }
    }
}

impl PicoContext {
    pub fn new() -> PicoContext {
        Default::default()
    }

    pub fn set_json(mut self, json: serde_json::Value) -> Self {
        self.input_json = Some(json);
        self
    }

    pub fn ns_add(&mut self, ns: &str) {
        let r = self
            .namespaced_variables
            .insert(ns.to_string(), HashMap::new());
        if let Some(original) = r {
            warn!("overwritten namespace {}", ns);
            trace!(" original: {:?}", original);
        }
    }

    pub fn ns_del(&mut self, ns: &str) {
        let r = self.namespaced_variables.remove(ns);
        if let Some(original) = r {
            info!("removed namespace {}", ns);
            trace!(" original: {:?}", original);
        }
    }

    pub fn ns_get(&self, ns: &str, key: &str) -> Option<&PicoValue> {
        self.namespaced_variables.get(ns).and_then(|hm| hm.get(key))
    }

    pub fn ns_set(&mut self, ns: &str, key: &str, value: &PicoValue) {
        self.namespaced_variables
            .get_mut(ns)
            .and_then(|hm| hm.insert(key.to_string(), value.clone()));
    }

    pub fn local_set(&mut self, key: &str, value: &PicoValue) {
        self.local_variables.insert(key.to_string(), value.clone());
    }

    pub fn local_get(&self, key: &str) -> Option<&PicoValue> {
        self.local_variables.get(key)
    }

    pub fn local_clear(&mut self) {
        self.local_variables.clear()
    }

    pub fn get_value(&self, key: &str) -> Option<&PicoValue> {
        match self.local_get(key) {
            Some(v) => Some(v),
            None => {
                if let Some(input_json) = &self.input_json {
                    trace!("Looking for key [{}] in input json", key);
                    let json_path = format!("/{}", key);
                    input_json.pointer(&json_path)
                } else {
                    None
                }
            }
        }
    }

    pub fn get_final_ctx(&mut self) -> &VariablesMap {
        self.variables
            .insert("input".to_string(), json!(&self.input_json));
        self.variables
            .insert("locals".to_string(), json!(&self.local_variables));
        self.variables
            .insert("namespaced".to_string(), json!(&self.namespaced_variables));

        &self.variables
    }
}

pub type PicoHashMap = HashMap<String, String>;