Skip to main content

zen_types/variable/
mod.rs

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