mf_expression/variable/
mod.rs

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