zen_expression/variable/
mod.rs

1use ahash::HashMap;
2use rust_decimal::prelude::Zero;
3use rust_decimal::Decimal;
4use serde_json::Value;
5use std::cell::RefCell;
6use std::collections::hash_map::Entry;
7use std::fmt::{Debug, Display, Formatter};
8use std::ops::Deref;
9use std::rc::Rc;
10
11mod conv;
12mod de;
13mod ser;
14mod types;
15
16pub use de::VariableDeserializer;
17pub use types::VariableType;
18
19pub(crate) type RcCell<T> = Rc<RefCell<T>>;
20#[derive(PartialEq, Eq)]
21pub enum Variable {
22    Null,
23    Bool(bool),
24    Number(Decimal),
25    String(Rc<str>),
26    Array(RcCell<Vec<Variable>>),
27    Object(RcCell<HashMap<String, Variable>>),
28}
29
30impl Variable {
31    pub fn from_array(arr: Vec<Variable>) -> Self {
32        Self::Array(Rc::new(RefCell::new(arr)))
33    }
34
35    pub fn from_object(obj: HashMap<String, Variable>) -> Self {
36        Self::Object(Rc::new(RefCell::new(obj)))
37    }
38
39    pub fn empty_object() -> Self {
40        Variable::Object(Default::default())
41    }
42
43    pub fn empty_array() -> Self {
44        Variable::Array(Default::default())
45    }
46
47    pub fn as_str(&self) -> Option<&str> {
48        match self {
49            Variable::String(s) => Some(s.as_ref()),
50            _ => None,
51        }
52    }
53
54    pub fn as_rc_str(&self) -> Option<Rc<str>> {
55        match self {
56            Variable::String(s) => Some(s.clone()),
57            _ => None,
58        }
59    }
60
61    pub fn as_array(&self) -> Option<RcCell<Vec<Variable>>> {
62        match self {
63            Variable::Array(arr) => Some(arr.clone()),
64            _ => None,
65        }
66    }
67
68    pub fn is_array(&self) -> bool {
69        match self {
70            Variable::Array(_) => true,
71            _ => false,
72        }
73    }
74
75    pub fn as_object(&self) -> Option<RcCell<HashMap<String, Variable>>> {
76        match self {
77            Variable::Object(obj) => Some(obj.clone()),
78            _ => None,
79        }
80    }
81
82    pub fn is_object(&self) -> bool {
83        match self {
84            Variable::Object(_) => true,
85            _ => false,
86        }
87    }
88
89    pub fn as_bool(&self) -> Option<bool> {
90        match self {
91            Variable::Bool(b) => Some(*b),
92            _ => None,
93        }
94    }
95
96    pub fn as_number(&self) -> Option<Decimal> {
97        match self {
98            Variable::Number(n) => Some(*n),
99            _ => None,
100        }
101    }
102
103    pub fn type_name(&self) -> &'static str {
104        match self {
105            Variable::Null => "null",
106            Variable::Bool(_) => "bool",
107            Variable::Number(_) => "number",
108            Variable::String(_) => "string",
109            Variable::Array(_) => "array",
110            Variable::Object(_) => "object",
111        }
112    }
113
114    pub fn to_value(&self) -> Value {
115        Value::from(self.shallow_clone())
116    }
117
118    pub fn dot(&self, key: &str) -> Option<Variable> {
119        key.split('.')
120            .try_fold(self.shallow_clone(), |var, part| match var {
121                Variable::Object(obj) => {
122                    let reference = obj.borrow();
123                    reference.get(part).map(|v| v.shallow_clone())
124                }
125                _ => None,
126            })
127    }
128
129    fn dot_head(&self, key: &str) -> Option<Variable> {
130        let mut parts = Vec::from_iter(key.split('.'));
131        parts.pop();
132
133        parts
134            .iter()
135            .try_fold(self.shallow_clone(), |var, part| match var {
136                Variable::Object(obj) => {
137                    let mut obj_ref = obj.borrow_mut();
138                    Some(match obj_ref.entry(part.to_string()) {
139                        Entry::Occupied(occ) => occ.get().shallow_clone(),
140                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
141                    })
142                }
143                _ => None,
144            })
145    }
146    pub fn dot_remove(&self, key: &str) -> Option<Variable> {
147        let last_part = key.split('.').last()?;
148        let head = self.dot_head(key)?;
149        let Variable::Object(object_ref) = head else {
150            return None;
151        };
152
153        let mut object = object_ref.borrow_mut();
154        object.remove(last_part)
155    }
156
157    pub fn dot_insert(&self, key: &str, variable: Variable) -> Option<Variable> {
158        let last_part = key.split('.').last()?;
159        let head = self.dot_head(key)?;
160        let Variable::Object(object_ref) = head else {
161            return None;
162        };
163
164        let mut object = object_ref.borrow_mut();
165        object.insert(last_part.to_string(), variable)
166    }
167
168    pub fn merge(&mut self, patch: &Variable) -> Variable {
169        let _ = merge_variables(self, patch, true, MergeStrategy::InPlace);
170
171        self.shallow_clone()
172    }
173
174    pub fn merge_clone(&mut self, patch: &Variable) -> Variable {
175        let mut new_self = self.shallow_clone();
176
177        let _ = merge_variables(&mut new_self, patch, true, MergeStrategy::CloneOnWrite);
178        new_self
179    }
180
181    pub fn shallow_clone(&self) -> Self {
182        match self {
183            Variable::Null => Variable::Null,
184            Variable::Bool(b) => Variable::Bool(*b),
185            Variable::Number(n) => Variable::Number(*n),
186            Variable::String(s) => Variable::String(s.clone()),
187            Variable::Array(a) => Variable::Array(a.clone()),
188            Variable::Object(o) => Variable::Object(o.clone()),
189        }
190    }
191
192    pub fn deep_clone(&self) -> Self {
193        match self {
194            Variable::Array(a) => {
195                let arr = a.borrow();
196                Variable::from_array(arr.iter().map(|v| v.deep_clone()).collect())
197            }
198            Variable::Object(o) => {
199                let obj = o.borrow();
200                Variable::from_object(
201                    obj.iter()
202                        .map(|(k, v)| (k.to_string(), v.deep_clone()))
203                        .collect(),
204                )
205            }
206            _ => self.shallow_clone(),
207        }
208    }
209
210    pub fn depth_clone(&self, depth: usize) -> Self {
211        match depth.is_zero() {
212            true => self.shallow_clone(),
213            false => match self {
214                Variable::Array(a) => {
215                    let arr = a.borrow();
216                    Variable::from_array(arr.iter().map(|v| v.depth_clone(depth - 1)).collect())
217                }
218                Variable::Object(o) => {
219                    let obj = o.borrow();
220                    Variable::from_object(
221                        obj.iter()
222                            .map(|(k, v)| (k.to_string(), v.depth_clone(depth - 1)))
223                            .collect(),
224                    )
225                }
226                _ => self.shallow_clone(),
227            },
228        }
229    }
230}
231
232impl Clone for Variable {
233    fn clone(&self) -> Self {
234        self.shallow_clone()
235    }
236}
237
238#[derive(Copy, Clone)]
239enum MergeStrategy {
240    InPlace,
241    CloneOnWrite,
242}
243
244fn merge_variables(
245    doc: &mut Variable,
246    patch: &Variable,
247    top_level: bool,
248    strategy: MergeStrategy,
249) -> bool {
250    if patch.is_array() && top_level {
251        *doc = patch.shallow_clone();
252        return true;
253    }
254
255    if !patch.is_object() && top_level {
256        return false;
257    }
258
259    if doc.is_object() && patch.is_object() {
260        let doc_ref = doc.as_object().unwrap();
261        let patch_ref = patch.as_object().unwrap();
262        if Rc::ptr_eq(&doc_ref, &patch_ref) {
263            return false;
264        }
265
266        let patch = patch_ref.borrow();
267        match strategy {
268            MergeStrategy::InPlace => {
269                let mut map = doc_ref.borrow_mut();
270                for (key, value) in patch.deref() {
271                    if value == &Variable::Null {
272                        map.remove(key.as_str());
273                    } else {
274                        let entry = map.entry(key.to_string()).or_insert(Variable::Null);
275                        merge_variables(entry, value, false, strategy);
276                    }
277                }
278
279                return true;
280            }
281            MergeStrategy::CloneOnWrite => {
282                let mut changed = false;
283                let mut new_map = None;
284
285                for (key, value) in patch.deref() {
286                    // Get or create the new map if we haven't yet
287                    let map = if let Some(ref mut m) = new_map {
288                        m
289                    } else {
290                        let m = doc_ref.borrow().clone();
291                        new_map = Some(m);
292                        new_map.as_mut().unwrap()
293                    };
294
295                    if value == &Variable::Null {
296                        // Remove null values
297                        if map.remove(key.as_str()).is_some() {
298                            changed = true;
299                        }
300                    } else {
301                        // Handle nested merging
302                        let entry = map.entry(key.to_string()).or_insert(Variable::Null);
303                        if merge_variables(entry, value, false, strategy) {
304                            changed = true;
305                        }
306                    }
307                }
308
309                // Only update doc if changes were made
310                if changed {
311                    if let Some(new_map) = new_map {
312                        *doc = Variable::Object(Rc::new(RefCell::new(new_map)));
313                    }
314                    return true;
315                }
316
317                return false;
318            }
319        }
320    } else {
321        let new_value = patch.shallow_clone();
322        if *doc != new_value {
323            *doc = new_value;
324            return true;
325        }
326
327        return false;
328    }
329}
330
331impl Display for Variable {
332    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
333        match self {
334            Variable::Null => write!(f, "null"),
335            Variable::Bool(b) => match *b {
336                true => write!(f, "true"),
337                false => write!(f, "false"),
338            },
339            Variable::Number(n) => write!(f, "{n}"),
340            Variable::String(s) => write!(f, "\"{s}\""),
341            Variable::Array(arr) => {
342                let arr = arr.borrow();
343                let s = arr
344                    .iter()
345                    .map(|v| v.to_string())
346                    .collect::<Vec<String>>()
347                    .join(",");
348                write!(f, "[{s}]")
349            }
350            Variable::Object(obj) => {
351                let obj = obj.borrow();
352                let s = obj
353                    .iter()
354                    .map(|(k, v)| format!("\"{k}\":{v}"))
355                    .collect::<Vec<String>>()
356                    .join(",");
357
358                write!(f, "{{{s}}}")
359            }
360        }
361    }
362}
363
364impl Debug for Variable {
365    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
366        write!(f, "{}", self)
367    }
368}