zen_types/variable/
mod.rs

1use crate::variable::ref_ser::RefSerializer;
2use ahash::HashMap;
3use rust_decimal::Decimal;
4use rust_decimal::prelude::Zero;
5use serde_json::Value;
6use std::any::Any;
7use std::cell::RefCell;
8use std::collections::hash_map::Entry;
9use std::fmt::{Debug, Display, Formatter};
10use std::ops::Deref;
11use std::rc::Rc;
12
13use crate::rcvalue::RcValue;
14pub use crate::variable::ref_deser::RefDeserializeError;
15use crate::variable::ref_deser::RefDeserializer;
16pub use de::VariableDeserializer;
17pub use impls::ToVariable;
18
19mod conv;
20mod de;
21mod impls;
22mod ref_deser;
23mod ref_ser;
24mod ser;
25
26pub(crate) type RcCell<T> = Rc<RefCell<T>>;
27
28pub type VariableMap = HashMap<Rc<str>, Variable>;
29
30pub enum Variable {
31    Null,
32    Bool(bool),
33    Number(Decimal),
34    String(Rc<str>),
35    Array(RcCell<Vec<Variable>>),
36    Object(RcCell<VariableMap>),
37    Dynamic(Rc<dyn DynamicVariable>),
38}
39
40pub trait DynamicVariable: Display {
41    fn type_name(&self) -> &'static str;
42
43    fn as_any(&self) -> &dyn Any;
44
45    fn to_value(&self) -> Value;
46}
47
48impl Variable {
49    pub fn from_array(arr: Vec<Self>) -> Self {
50        Self::Array(Rc::new(RefCell::new(arr)))
51    }
52
53    pub fn serialize_ref(&self) -> RcValue {
54        RefSerializer::new().serialize(self)
55    }
56
57    pub fn deserialize_ref(serialized: RcValue) -> Result<Self, RefDeserializeError> {
58        RefDeserializer::new().deserialize(serialized)
59    }
60
61    pub fn from_object(obj: HashMap<Rc<str>, Self>) -> Self {
62        Self::Object(Rc::new(RefCell::new(obj)))
63    }
64
65    pub fn empty_object() -> Self {
66        Variable::Object(Default::default())
67    }
68
69    pub fn empty_array() -> Self {
70        Variable::Array(Default::default())
71    }
72
73    pub fn as_str(&self) -> Option<&str> {
74        match self {
75            Variable::String(s) => Some(s.as_ref()),
76            _ => None,
77        }
78    }
79
80    pub fn as_rc_str(&self) -> Option<Rc<str>> {
81        match self {
82            Variable::String(s) => Some(s.clone()),
83            _ => None,
84        }
85    }
86
87    pub fn as_array(&self) -> Option<RcCell<Vec<Variable>>> {
88        match self {
89            Variable::Array(arr) => Some(arr.clone()),
90            _ => None,
91        }
92    }
93
94    pub fn is_array(&self) -> bool {
95        match self {
96            Variable::Array(_) => true,
97            _ => false,
98        }
99    }
100
101    pub fn as_object(&self) -> Option<RcCell<HashMap<Rc<str>, Variable>>> {
102        match self {
103            Variable::Object(obj) => Some(obj.clone()),
104            _ => None,
105        }
106    }
107
108    pub fn is_object(&self) -> bool {
109        match self {
110            Variable::Object(_) => true,
111            _ => false,
112        }
113    }
114
115    pub fn as_bool(&self) -> Option<bool> {
116        match self {
117            Variable::Bool(b) => Some(*b),
118            _ => None,
119        }
120    }
121
122    pub fn as_number(&self) -> Option<Decimal> {
123        match self {
124            Variable::Number(n) => Some(*n),
125            _ => None,
126        }
127    }
128
129    pub fn type_name(&self) -> &'static str {
130        match self {
131            Variable::Null => "null",
132            Variable::Bool(_) => "bool",
133            Variable::Number(_) => "number",
134            Variable::String(_) => "string",
135            Variable::Array(_) => "array",
136            Variable::Object(_) => "object",
137            Variable::Dynamic(d) => d.type_name(),
138        }
139    }
140
141    pub fn dynamic<T: DynamicVariable + 'static>(&self) -> Option<&T> {
142        match self {
143            Variable::Dynamic(d) => d.as_any().downcast_ref::<T>(),
144            _ => None,
145        }
146    }
147
148    pub fn to_value(&self) -> Value {
149        Value::from(self.shallow_clone())
150    }
151
152    pub fn dot(&self, key: &str) -> Option<Variable> {
153        key.split('.')
154            .try_fold(self.shallow_clone(), |var, part| match var {
155                Variable::Object(obj) => {
156                    let reference = obj.borrow();
157                    reference.get(part).map(|v| v.shallow_clone())
158                }
159                _ => None,
160            })
161    }
162
163    fn dot_head_detach(&self, key: &str) -> (Variable, Option<Variable>) {
164        let mut parts = Vec::from_iter(key.split('.'));
165        parts.pop();
166
167        let cloned_self = self.depth_clone(1);
168        let head = parts
169            .iter()
170            .try_fold(cloned_self.shallow_clone(), |var, part| match var {
171                Variable::Object(obj) => {
172                    let mut obj_ref = obj.borrow_mut();
173                    Some(match obj_ref.entry(Rc::from(*part)) {
174                        Entry::Occupied(mut occ) => {
175                            let var = occ.get();
176                            let new_obj = match var {
177                                Variable::Object(_) => var.depth_clone(1),
178                                _ => Variable::empty_object(),
179                            };
180
181                            occ.insert(new_obj.shallow_clone());
182                            new_obj
183                        }
184                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
185                    })
186                }
187                _ => None,
188            });
189
190        (cloned_self, head)
191    }
192
193    pub fn dot_remove(&self, key: &str) -> Option<Variable> {
194        let mut parts = key.split('.');
195        let last_part = parts.next_back()?;
196        let head = parts.try_fold(self.shallow_clone(), |var, part| match var {
197            Variable::Object(obj) => {
198                let mut obj_ref = obj.borrow_mut();
199                Some(match obj_ref.entry(Rc::from(part)) {
200                    Entry::Occupied(occ) => occ.get().shallow_clone(),
201                    Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
202                })
203            }
204            _ => None,
205        })?;
206        let Variable::Object(object_ref) = head else {
207            return None;
208        };
209
210        let mut object = object_ref.borrow_mut();
211        object.remove(last_part)
212    }
213
214    pub fn dot_insert(&self, key: &str, variable: Variable) -> Option<Variable> {
215        let mut parts = key.split('.');
216        let last_part = parts.next_back()?;
217        let head = parts.try_fold(self.shallow_clone(), |var, part| match var {
218            Variable::Object(obj) => {
219                let mut obj_ref = obj.borrow_mut();
220                Some(match obj_ref.entry(Rc::from(part)) {
221                    Entry::Occupied(occ) => occ.get().shallow_clone(),
222                    Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
223                })
224            }
225            _ => None,
226        })?;
227        let Variable::Object(object_ref) = head else {
228            return None;
229        };
230
231        let mut object = object_ref.borrow_mut();
232        object.insert(Rc::from(last_part), variable)
233    }
234
235    pub fn dot_insert_detached(&self, key: &str, variable: Variable) -> Option<Variable> {
236        let last_part = key.split('.').last()?;
237        let (new_var, head_opt) = self.dot_head_detach(key);
238        let head = head_opt?;
239        let Variable::Object(object_ref) = head else {
240            return None;
241        };
242
243        let mut object = object_ref.borrow_mut();
244        object.insert(Rc::from(last_part), variable);
245        Some(new_var)
246    }
247
248    pub fn merge(&mut self, patch: &Variable) -> Variable {
249        let _ = merge_variables(self, patch, true, MergeStrategy::InPlace);
250
251        self.shallow_clone()
252    }
253
254    pub fn merge_clone(&mut self, patch: &Variable) -> Variable {
255        let mut new_self = self.shallow_clone();
256
257        let _ = merge_variables(&mut new_self, patch, true, MergeStrategy::CloneOnWrite);
258        new_self
259    }
260
261    pub fn shallow_clone(&self) -> Self {
262        match self {
263            Variable::Null => Variable::Null,
264            Variable::Bool(b) => Variable::Bool(*b),
265            Variable::Number(n) => Variable::Number(*n),
266            Variable::String(s) => Variable::String(s.clone()),
267            Variable::Array(a) => Variable::Array(a.clone()),
268            Variable::Object(o) => Variable::Object(o.clone()),
269            Variable::Dynamic(d) => Variable::Dynamic(d.clone()),
270        }
271    }
272
273    pub fn deep_clone(&self) -> Self {
274        match self {
275            Variable::Array(a) => {
276                let arr = a.borrow();
277                Variable::from_array(arr.iter().map(|v| v.deep_clone()).collect())
278            }
279            Variable::Object(o) => {
280                let obj = o.borrow();
281                Variable::from_object(
282                    obj.iter()
283                        .map(|(k, v)| (k.clone(), v.deep_clone()))
284                        .collect(),
285                )
286            }
287            _ => self.shallow_clone(),
288        }
289    }
290
291    pub fn depth_clone(&self, depth: usize) -> Self {
292        match depth.is_zero() {
293            true => self.shallow_clone(),
294            false => match self {
295                Variable::Array(a) => {
296                    let arr = a.borrow();
297                    Variable::from_array(arr.iter().map(|v| v.depth_clone(depth - 1)).collect())
298                }
299                Variable::Object(o) => {
300                    let obj = o.borrow();
301                    Variable::from_object(
302                        obj.iter()
303                            .map(|(k, v)| (k.clone(), v.depth_clone(depth - 1)))
304                            .collect(),
305                    )
306                }
307                _ => self.shallow_clone(),
308            },
309        }
310    }
311}
312
313impl Clone for Variable {
314    fn clone(&self) -> Self {
315        self.shallow_clone()
316    }
317}
318
319#[derive(Copy, Clone)]
320enum MergeStrategy {
321    InPlace,
322    CloneOnWrite,
323}
324
325fn merge_variables(
326    doc: &mut Variable,
327    patch: &Variable,
328    top_level: bool,
329    strategy: MergeStrategy,
330) -> bool {
331    if patch.is_array() && top_level {
332        *doc = patch.shallow_clone();
333        return true;
334    }
335
336    if !patch.is_object() && top_level {
337        return false;
338    }
339
340    if doc.is_object() && patch.is_object() {
341        let doc_ref = doc.as_object().unwrap();
342        let patch_ref = patch.as_object().unwrap();
343        if Rc::ptr_eq(&doc_ref, &patch_ref) {
344            return false;
345        }
346
347        let patch = patch_ref.borrow();
348        match strategy {
349            MergeStrategy::InPlace => {
350                let mut map = doc_ref.borrow_mut();
351                for (key, value) in patch.deref() {
352                    if value == &Variable::Null {
353                        map.remove(key);
354                    } else {
355                        let entry = map.entry(key.clone()).or_insert(Variable::Null);
356                        merge_variables(entry, value, false, strategy);
357                    }
358                }
359
360                return true;
361            }
362            MergeStrategy::CloneOnWrite => {
363                let mut changed = false;
364                let mut new_map = None;
365
366                for (key, value) in patch.deref() {
367                    // Get or create the new map if we haven't yet
368                    let map = if let Some(ref mut m) = new_map {
369                        m
370                    } else {
371                        let m = doc_ref.borrow().clone();
372                        new_map = Some(m);
373                        new_map.as_mut().unwrap()
374                    };
375
376                    if value == &Variable::Null {
377                        // Remove null values
378                        if map.remove(key).is_some() {
379                            changed = true;
380                        }
381                    } else {
382                        // Handle nested merging
383                        let entry = map.entry(key.clone()).or_insert(Variable::Null);
384                        if merge_variables(entry, value, false, strategy) {
385                            changed = true;
386                        }
387                    }
388                }
389
390                // Only update doc if changes were made
391                if changed {
392                    if let Some(new_map) = new_map {
393                        *doc = Variable::Object(Rc::new(RefCell::new(new_map)));
394                    }
395                    return true;
396                }
397
398                return false;
399            }
400        }
401    } else {
402        let new_value = patch.shallow_clone();
403        if *doc != new_value {
404            *doc = new_value;
405            return true;
406        }
407
408        return false;
409    }
410}
411
412impl Default for Variable {
413    fn default() -> Self {
414        Variable::Null
415    }
416}
417
418impl Display for Variable {
419    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
420        match self {
421            Variable::Null => write!(f, "null"),
422            Variable::Bool(b) => match *b {
423                true => write!(f, "true"),
424                false => write!(f, "false"),
425            },
426            Variable::Number(n) => write!(f, "{n}"),
427            Variable::String(s) => write!(f, "\"{s}\""),
428            Variable::Array(arr) => {
429                let arr = arr.borrow();
430                let s = arr
431                    .iter()
432                    .map(|v| v.to_string())
433                    .collect::<Vec<String>>()
434                    .join(",");
435                write!(f, "[{s}]")
436            }
437            Variable::Object(obj) => {
438                let obj = obj.borrow();
439                let s = obj
440                    .iter()
441                    .map(|(k, v)| format!("\"{k}\":{v}"))
442                    .collect::<Vec<String>>()
443                    .join(",");
444
445                write!(f, "{{{s}}}")
446            }
447            Variable::Dynamic(d) => write!(f, "{d}"),
448        }
449    }
450}
451
452impl Debug for Variable {
453    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
454        write!(f, "{}", self)
455    }
456}
457
458impl PartialEq for Variable {
459    fn eq(&self, other: &Self) -> bool {
460        match (&self, &other) {
461            (Variable::Null, Variable::Null) => true,
462            (Variable::Bool(b1), Variable::Bool(b2)) => b1 == b2,
463            (Variable::Number(n1), Variable::Number(n2)) => n1 == n2,
464            (Variable::String(s1), Variable::String(s2)) => s1 == s2,
465            (Variable::Array(a1), Variable::Array(a2)) => a1 == a2,
466            (Variable::Object(obj1), Variable::Object(obj2)) => obj1 == obj2,
467            (Variable::Dynamic(d1), Variable::Dynamic(d2)) => Rc::ptr_eq(d1, d2),
468            _ => false,
469        }
470    }
471}
472
473impl Eq for Variable {}
474
475#[cfg(test)]
476mod tests {
477    use crate::variable::Variable;
478    use rust_decimal_macros::dec;
479    use serde_json::json;
480
481    #[test]
482    fn insert_detached() {
483        let some_data: Variable = json!({ "customer": { "firstName": "John" }}).into();
484
485        let a_a = some_data
486            .dot_insert_detached("a.a", Variable::Number(dec!(1)))
487            .unwrap();
488        let a_b = a_a
489            .dot_insert_detached("a.b", Variable::Number(dec!(2)))
490            .unwrap();
491        let a_c = a_b
492            .dot_insert_detached("a.c", Variable::Number(dec!(3)))
493            .unwrap();
494
495        assert_eq!(a_a.dot("a"), Some(Variable::from(json!({ "a": 1 }))));
496        assert_eq!(
497            a_b.dot("a"),
498            Some(Variable::from(json!({ "a": 1, "b": 2 })))
499        );
500        assert_eq!(
501            a_c.dot("a"),
502            Some(Variable::from(json!({ "a": 1, "b": 2, "c": 3 })))
503        );
504    }
505}