zen-types 0.55.1

Zen Core Types
Documentation
use crate::variable_type::VariableType;
use ahash::{HashMap, HashMapExt};
use rust_decimal::prelude::Zero;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::rc::Rc;

impl VariableType {
    pub fn iterator(&self) -> Option<Rc<VariableType>> {
        match self {
            VariableType::Array(item) => Some(item.clone()),
            VariableType::Interval => Some(Rc::new(VariableType::Number)),
            _ => None,
        }
    }

    pub fn as_const_str(&self) -> Option<Rc<str>> {
        match self {
            VariableType::Const(s) => Some(s.clone()),
            _ => None,
        }
    }

    pub fn get(&self, key: &str) -> VariableType {
        match self {
            VariableType::Object(obj) => {
                let obj = obj.borrow();
                obj.get(key).cloned().unwrap_or(VariableType::Any)
            }
            _ => VariableType::Null,
        }
    }

    pub fn satisfies(&self, constraint: &Self) -> bool {
        match (self, constraint) {
            (VariableType::Any, _) | (_, VariableType::Any) => true,
            (VariableType::Null, VariableType::Null) => true,
            (VariableType::Bool, VariableType::Bool) => true,
            (VariableType::String, VariableType::String) => true,
            (VariableType::Number, VariableType::Number) => true,
            (VariableType::Date, VariableType::Date) => true,
            (VariableType::Number, VariableType::Date) => true,
            (_, VariableType::Date) if self.widen().is_string() => true,
            (VariableType::Interval, VariableType::Interval) => true,
            (VariableType::Array(a1), VariableType::Array(a2)) => a1.satisfies(a2),
            (VariableType::Object(o1), VariableType::Object(o2)) => {
                let o1 = o1.borrow();
                let o2 = o2.borrow();

                o1.iter()
                    .all(|(k, v)| o2.get(k).is_some_and(|tv| v.satisfies(tv)))
            }

            (VariableType::Const(c1), VariableType::Const(c2)) => c1 == c2,
            (VariableType::Const(c), VariableType::Enum(_, e)) => e.iter().any(|e| e == c),
            (VariableType::Const(_), VariableType::String) => true,
            (VariableType::String, VariableType::Const(_)) => true,

            (VariableType::Enum(_, e1), VariableType::Enum(_, e2)) => {
                e1.iter().all(|c| e2.contains(c))
            }
            (VariableType::Enum(_, e), VariableType::Const(c)) => e.iter().all(|i| i == c),
            (VariableType::Enum(_, _), VariableType::String) => true,
            (VariableType::String, VariableType::Enum(_, _)) => true,

            (_, _) => false,
        }
    }

    pub fn is_array(&self) -> bool {
        match self {
            VariableType::Any | VariableType::Array(_) => true,
            _ => false,
        }
    }

    pub fn is_iterable(&self) -> bool {
        match self {
            VariableType::Any | VariableType::Interval | VariableType::Array(_) => true,
            _ => false,
        }
    }

    pub fn is_string(&self) -> bool {
        match self {
            VariableType::String => true,
            _ => false,
        }
    }

    pub fn is_object(&self) -> bool {
        match self {
            VariableType::Any | VariableType::Object(_) => true,
            _ => false,
        }
    }

    pub fn is_null(&self) -> bool {
        match self {
            VariableType::Null => true,
            _ => false,
        }
    }

    pub fn widen(&self) -> Self {
        match self {
            VariableType::Const(_) | VariableType::Enum(_, _) => VariableType::String,
            _ => self.clone(),
        }
    }

    pub fn merge(&self, other: &Self) -> Self {
        match (self, other) {
            (VariableType::Any, _) | (_, VariableType::Any) => VariableType::Any,
            (VariableType::Null, VariableType::Null) => VariableType::Null,
            (VariableType::Bool, VariableType::Bool) => VariableType::Bool,
            (VariableType::String, VariableType::String) => VariableType::String,
            (VariableType::Number, VariableType::Number) => VariableType::Number,
            (VariableType::Date, VariableType::Date) => VariableType::Date,
            (VariableType::Interval, VariableType::Interval) => VariableType::Interval,
            (VariableType::Array(a1), VariableType::Array(a2)) => {
                if Rc::ptr_eq(a1, a2) {
                    VariableType::Array(a1.clone())
                } else {
                    VariableType::Array(Rc::new(a1.as_ref().merge(a2.as_ref())))
                }
            }

            (VariableType::Object(o1), VariableType::Object(o2)) => {
                let o1 = o1.borrow();
                let o2 = o2.borrow();

                let mut merged = HashMap::with_capacity(o1.len().max(o2.len()));
                for (k, v) in o1.iter() {
                    merged.insert(k.clone(), v.clone());
                }

                for (k, v) in o2.iter() {
                    match merged.entry(k.clone()) {
                        Entry::Occupied(mut entry) => {
                            let current = entry.get();
                            let merged_value = current.merge(v);
                            entry.insert(merged_value);
                        }
                        Entry::Vacant(entry) => {
                            entry.insert(v.clone());
                        }
                    }
                }

                VariableType::Object(Rc::new(RefCell::new(merged)))
            }

            (VariableType::Const(c), VariableType::Enum(_, values)) => {
                let mut merged = values.clone();
                if !merged.contains(c) {
                    merged.push(c.clone());
                }

                VariableType::Enum(None, merged)
            }
            (VariableType::Const(c1), VariableType::Const(c2)) => {
                if Rc::ptr_eq(c1, c2) || c1 == c2 {
                    VariableType::Const(c1.clone())
                } else {
                    VariableType::Enum(None, vec![c1.clone(), c2.clone()])
                }
            }
            (VariableType::Const(_), VariableType::String)
            | (VariableType::String, VariableType::Const(_)) => VariableType::String,

            (VariableType::Enum(n1, a), VariableType::Enum(n2, b)) => {
                let mut merged = a.clone();
                for val in b {
                    if !merged.contains(val) {
                        merged.push(val.clone());
                    }
                }

                let name = match (n1, n2) {
                    (Some(n1), Some(n2)) => Some(Rc::<str>::from(format!("{} | {}", n1, n2))),
                    _ => None,
                };

                VariableType::Enum(name, merged)
            }

            (VariableType::Enum(_, values), VariableType::Const(c)) => {
                let mut merged = values.clone();
                if !merged.contains(c) {
                    merged.push(c.clone());
                }
                VariableType::Enum(None, merged)
            }

            (VariableType::Enum(_, _), VariableType::String)
            | (VariableType::String, VariableType::Enum(_, _)) => VariableType::String,

            (_, _) => VariableType::Any,
        }
    }

    pub fn shallow_clone(&self) -> Self {
        match self {
            VariableType::Any => VariableType::Any,
            VariableType::Null => VariableType::Null,
            VariableType::Bool => VariableType::Bool,
            VariableType::String => VariableType::String,
            VariableType::Number => VariableType::Number,
            VariableType::Date => VariableType::Date,
            VariableType::Interval => VariableType::Interval,
            VariableType::Array(arr) => VariableType::Array(arr.clone()),
            VariableType::Object(obj) => VariableType::Object(obj.clone()),
            VariableType::Const(c) => VariableType::Const(c.clone()),
            VariableType::Enum(name, options) => VariableType::Enum(name.clone(), options.clone()),
        }
    }

    pub fn dot_head(&self, key: &str) -> Option<Self> {
        let mut parts = Vec::from_iter(key.split('.'));
        parts.pop();

        parts
            .iter()
            .try_fold(self.shallow_clone(), |var, part| match var {
                VariableType::Object(obj) => {
                    let mut obj_ref = obj.borrow_mut();
                    Some(match obj_ref.entry(Rc::from(*part)) {
                        Entry::Occupied(occ) => occ.get().shallow_clone(),
                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
                    })
                }
                _ => None,
            })
    }

    pub fn dot_head_detach(&self, key: &str) -> (Self, Option<Self>) {
        let mut parts = Vec::from_iter(key.split('.'));
        parts.pop();

        let cloned_self = self.depth_clone(1);
        let head = parts
            .iter()
            .try_fold(cloned_self.shallow_clone(), |var, part| match var {
                VariableType::Object(obj) => {
                    let mut obj_ref = obj.borrow_mut();
                    Some(match obj_ref.entry(Rc::from(*part)) {
                        Entry::Occupied(mut occ) => {
                            let var = occ.get();
                            let new_obj = match var {
                                VariableType::Object(_) => var.depth_clone(1),
                                _ => VariableType::empty_object(),
                            };

                            occ.insert(new_obj.shallow_clone());
                            new_obj
                        }
                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
                    })
                }
                _ => None,
            });

        (cloned_self, head)
    }

    pub fn depth_clone(&self, depth: usize) -> Self {
        match depth.is_zero() {
            true => self.shallow_clone(),
            false => match self {
                VariableType::Object(o) => {
                    let obj = o.borrow();
                    VariableType::Object(Rc::new(RefCell::new(
                        obj.iter()
                            .map(|(k, v)| (k.clone(), v.depth_clone(depth - 1)))
                            .collect(),
                    )))
                }
                _ => self.shallow_clone(),
            },
        }
    }

    pub fn empty_object() -> Self {
        VariableType::Object(Rc::new(RefCell::new(HashMap::new())))
    }

    pub fn dot_insert_detached(&self, key: &str, variable: Self) -> Option<Self> {
        let last_part = key.split('.').last()?;
        let (new_var, head_opt) = self.dot_head_detach(key);
        let head = head_opt?;
        let VariableType::Object(object_ref) = head else {
            return None;
        };

        let mut object = object_ref.borrow_mut();
        object.insert(Rc::from(last_part), variable);
        Some(new_var)
    }

    pub fn dot_insert(&self, key: &str, variable: Self) -> Option<Self> {
        let last_part = key.split('.').last()?;
        let head = self.dot_head(key)?;
        let Self::Object(object_ref) = head else {
            return None;
        };

        let mut object = object_ref.borrow_mut();
        object.insert(Rc::from(last_part), variable)
    }

    pub fn dot(&self, key: &str) -> Option<Self> {
        key.split('.')
            .try_fold(self.shallow_clone(), |var, part| match var {
                Self::Object(obj) => {
                    let reference = obj.borrow();
                    reference.get(part).map(|v| v.shallow_clone())
                }
                _ => None,
            })
    }
}