Skip to main content

matugen_parser/
context.rs

1use std::collections::HashMap;
2
3use indexmap::IndexMap;
4
5use crate::value::Value;
6
7#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct RuntimeContext {
10    global: Context,
11    pub scopes: Vec<HashMap<String, Value>>,
12}
13
14impl<'a> RuntimeContext {
15    pub fn new(global: Context) -> Self {
16        Self {
17            global,
18            scopes: Vec::new(),
19        }
20    }
21
22    pub fn resolve_path<I>(&self, path: I) -> Option<Value>
23    where
24        I: IntoIterator<Item = &'a str>,
25    {
26        let mut iter = path.into_iter();
27        let first = iter.next()?;
28        let mut current = self.get_from_scopes(first)?;
29
30        for key in iter {
31            current = match current {
32                Value::Map(map) => map.get(key)?.clone(),
33                _ => {
34                    return None;
35                }
36            };
37        }
38
39        Some(current)
40    }
41
42    fn get_from_scopes(&self, key: &str) -> Option<Value> {
43        for scope in self.scopes.iter().rev() {
44            if let Some(val) = scope.get(key) {
45                return Some(val.clone());
46            }
47        }
48        None
49    }
50
51    pub fn push_scope(&mut self) {
52        self.scopes.push(HashMap::new());
53    }
54
55    pub fn pop_scope(&mut self) {
56        self.scopes.pop();
57    }
58
59    pub fn insert(&mut self, key: impl Into<String>, value: Value) {
60        if let Some(scope) = self.scopes.last_mut() {
61            scope.insert(key.into(), value);
62        } else {
63            self.scopes.push(HashMap::from([(key.into(), value)]));
64        }
65    }
66}
67
68#[derive(Debug, Clone)]
69pub struct Context {
70    pub data: IndexMap<String, Value>,
71}
72
73impl Default for Context {
74    fn default() -> Self {
75        Self::new()
76    }
77}
78
79impl Context {
80    pub fn new() -> Self {
81        Self {
82            data: IndexMap::new(),
83        }
84    }
85
86    pub fn merge(&mut self, incoming: &IndexMap<String, Value>) {
87        merge_nested(&mut self.data, incoming);
88    }
89
90    pub fn merge_value(&mut self, incoming: Value) {
91        match incoming {
92            Value::Map(map) => {
93                merge_nested(&mut self.data, &map);
94            }
95            _ => panic!(""),
96        }
97    }
98
99    fn json_to_value_map(&self, json: serde_json::Value) -> Option<IndexMap<String, Value>> {
100        match Value::from(json) {
101            Value::Map(map) => Some(map),
102            _ => None,
103        }
104    }
105
106    pub fn merge_json(&mut self, json: serde_json::Value) {
107        if let Some(map) = self.json_to_value_map(json) {
108            self.merge(&map);
109        } else {
110            panic!("Expected a JSON object to merge into context.");
111        }
112    }
113
114    pub fn delete_key(&mut self, key: &str) {
115        self.data.swap_remove(key);
116    }
117
118    pub fn remove_path<'a, I>(&mut self, path: I) -> bool
119    where
120        I: IntoIterator<Item = &'a str>,
121    {
122        let mut iter = path.into_iter();
123        let Some(first) = iter.next() else {
124            return false;
125        };
126
127        let mut current = &mut self.data;
128        let mut last_key = first;
129
130        for key in iter {
131            match current.get_mut(last_key) {
132                Some(Value::Map(map)) => {
133                    current = map;
134                    last_key = key;
135                }
136                _ => return false,
137            }
138        }
139
140        current.swap_remove(last_key).is_some()
141    }
142
143    pub fn data(&self) -> &IndexMap<String, Value> {
144        &self.data
145    }
146}
147
148fn merge_nested(target: &mut IndexMap<String, Value>, source: &IndexMap<String, Value>) {
149    for (key, value) in source {
150        match (target.get_mut(key), value) {
151            (Some(Value::Map(target_obj)), Value::Map(source_obj)) => {
152                merge_nested(target_obj, source_obj);
153            }
154            _ => {
155                target.insert(key.clone(), value.clone());
156            }
157        }
158    }
159}