Skip to main content

jetro_core/eval/
value.rs

1/// Internal value type for the v2 evaluator.
2///
3/// Every compound node (Arr, Obj) is `Arc`-wrapped so that `Val::clone()`
4/// is a single atomic refcount bump — no deep copies during traversal.
5///
6/// The API boundary (`evaluate()`) converts `&serde_json::Value → Val`
7/// once on entry and `Val → serde_json::Value` once on exit.
8use std::sync::Arc;
9use indexmap::IndexMap;
10use serde_json::{Map, Number};
11
12// ── Core type ─────────────────────────────────────────────────────────────────
13
14#[derive(Clone, Debug)]
15pub enum Val {
16    Null,
17    Bool(bool),
18    Int(i64),
19    Float(f64),
20    Str(Arc<str>),
21    Arr(Arc<Vec<Val>>),
22    Obj(Arc<IndexMap<Arc<str>, Val>>),
23}
24
25// ── Constants (avoid heap allocation for common nulls) ────────────────────────
26
27thread_local! {
28    static NULL_VAL: Val = Val::Null;
29}
30
31// ── Cheap structural operations ───────────────────────────────────────────────
32
33impl Val {
34    /// O(1) field lookup — returns a clone of the child (cheap: Arc bump for Arr/Obj, copy for scalars).
35    #[inline]
36    pub fn get_field(&self, key: &str) -> Val {
37        match self {
38            Val::Obj(m) => m.get(key).cloned().unwrap_or(Val::Null),
39            _           => Val::Null,
40        }
41    }
42
43    /// O(1) index — returns a clone of the element (cheap: Arc bump for Arr/Obj).
44    #[inline]
45    pub fn get_index(&self, i: i64) -> Val {
46        match self {
47            Val::Arr(a) => {
48                let idx = if i < 0 {
49                    a.len().saturating_sub(i.unsigned_abs() as usize)
50                } else { i as usize };
51                a.get(idx).cloned().unwrap_or(Val::Null)
52            }
53            _ => Val::Null,
54        }
55    }
56
57    #[inline] pub fn is_null(&self)   -> bool { matches!(self, Val::Null) }
58    #[inline] pub fn is_bool(&self)   -> bool { matches!(self, Val::Bool(_)) }
59    #[inline] pub fn is_number(&self) -> bool { matches!(self, Val::Int(_) | Val::Float(_)) }
60    #[inline] pub fn is_string(&self) -> bool { matches!(self, Val::Str(_)) }
61    #[inline] pub fn is_array(&self)  -> bool { matches!(self, Val::Arr(_)) }
62    #[inline] pub fn is_object(&self) -> bool { matches!(self, Val::Obj(_)) }
63
64    #[inline]
65    pub fn as_bool(&self) -> Option<bool> {
66        if let Val::Bool(b) = self { Some(*b) } else { None }
67    }
68
69    #[inline]
70    pub fn as_i64(&self) -> Option<i64> {
71        match self { Val::Int(n) => Some(*n), Val::Float(f) => Some(*f as i64), _ => None }
72    }
73
74    #[inline]
75    pub fn as_f64(&self) -> Option<f64> {
76        match self { Val::Float(f) => Some(*f), Val::Int(n) => Some(*n as f64), _ => None }
77    }
78
79    #[inline]
80    pub fn as_str(&self) -> Option<&str> {
81        if let Val::Str(s) = self { Some(s) } else { None }
82    }
83
84    #[inline]
85    pub fn as_array(&self) -> Option<&[Val]> {
86        if let Val::Arr(a) = self { Some(a) } else { None }
87    }
88
89    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Val>> {
90        if let Val::Arr(a) = self { Some(Arc::make_mut(a)) } else { None }
91    }
92
93    #[inline]
94    pub fn as_object(&self) -> Option<&IndexMap<Arc<str>, Val>> {
95        if let Val::Obj(m) = self { Some(m) } else { None }
96    }
97
98    pub fn as_object_mut(&mut self) -> Option<&mut IndexMap<Arc<str>, Val>> {
99        if let Val::Obj(m) = self { Some(Arc::make_mut(m)) } else { None }
100    }
101
102    /// Read-only get (serde_json compat shim).
103    pub fn get(&self, key: &str) -> Option<&Val> {
104        match self {
105            Val::Obj(m) => m.get(key),
106            _           => None,
107        }
108    }
109
110    pub fn type_name(&self) -> &'static str {
111        match self {
112            Val::Null    => "null",
113            Val::Bool(_) => "bool",
114            Val::Int(_) | Val::Float(_) => "number",
115            Val::Str(_)  => "string",
116            Val::Arr(_)  => "array",
117            Val::Obj(_)  => "object",
118        }
119    }
120
121    /// Consume self and produce a mutable Vec (clone only if shared).
122    pub fn into_vec(self) -> Option<Vec<Val>> {
123        if let Val::Arr(a) = self {
124            Some(Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()))
125        } else { None }
126    }
127
128    /// Consume self and produce a mutable map (clone only if shared).
129    pub fn into_map(self) -> Option<IndexMap<Arc<str>, Val>> {
130        if let Val::Obj(m) = self {
131            Some(Arc::try_unwrap(m).unwrap_or_else(|m| (*m).clone()))
132        } else { None }
133    }
134
135    /// Build a Val::Arr from a Vec without an extra allocation.
136    #[inline]
137    pub fn arr(v: Vec<Val>) -> Self { Val::Arr(Arc::new(v)) }
138
139    /// Build a Val::Obj from an IndexMap without an extra allocation.
140    #[inline]
141    pub fn obj(m: IndexMap<Arc<str>, Val>) -> Self { Val::Obj(Arc::new(m)) }
142
143    /// Intern a string key.
144    #[inline]
145    pub fn key(s: &str) -> Arc<str> { Arc::from(s) }
146}
147
148// ── serde_json::Value ↔ Val conversions ───────────────────────────────────────
149
150impl From<&serde_json::Value> for Val {
151    fn from(v: &serde_json::Value) -> Self {
152        match v {
153            serde_json::Value::Null      => Val::Null,
154            serde_json::Value::Bool(b)   => Val::Bool(*b),
155            serde_json::Value::Number(n) => {
156                if let Some(i) = n.as_i64() { Val::Int(i) }
157                else { Val::Float(n.as_f64().unwrap_or(0.0)) }
158            }
159            serde_json::Value::String(s) => Val::Str(Arc::from(s.as_str())),
160            serde_json::Value::Array(a)  => Val::Arr(Arc::new(a.iter().map(Val::from).collect())),
161            serde_json::Value::Object(m) => Val::Obj(Arc::new(
162                m.iter().map(|(k, v)| (Arc::from(k.as_str()), Val::from(v))).collect()
163            )),
164        }
165    }
166}
167
168impl From<Val> for serde_json::Value {
169    fn from(v: Val) -> Self {
170        match v {
171            Val::Null    => serde_json::Value::Null,
172            Val::Bool(b) => serde_json::Value::Bool(b),
173            Val::Int(n)  => serde_json::Value::Number(n.into()),
174            Val::Float(f) => serde_json::Value::Number(
175                Number::from_f64(f).unwrap_or_else(|| 0.into())
176            ),
177            Val::Str(s)  => serde_json::Value::String(s.to_string()),
178            Val::Arr(a)  => serde_json::Value::Array(
179                Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())
180                    .into_iter().map(serde_json::Value::from).collect()
181            ),
182            Val::Obj(m)  => {
183                let map: Map<String, serde_json::Value> =
184                    Arc::try_unwrap(m).unwrap_or_else(|m| (*m).clone())
185                        .into_iter()
186                        .map(|(k, v)| (k.to_string(), serde_json::Value::from(v)))
187                        .collect();
188                serde_json::Value::Object(map)
189            }
190        }
191    }
192}
193
194// ── PartialEq for comparison ──────────────────────────────────────────────────
195
196impl PartialEq for Val {
197    fn eq(&self, other: &Self) -> bool {
198        match (self, other) {
199            (Val::Null,    Val::Null)    => true,
200            (Val::Bool(a), Val::Bool(b)) => a == b,
201            (Val::Str(a),  Val::Str(b))  => a == b,
202            (Val::Int(a),  Val::Int(b))  => a == b,
203            (Val::Float(a), Val::Float(b)) => a == b,
204            (Val::Int(a),  Val::Float(b)) => (*a as f64) == *b,
205            (Val::Float(a), Val::Int(b))  => *a == (*b as f64),
206            _ => false,
207        }
208    }
209}
210
211impl Eq for Val {}
212
213impl std::hash::Hash for Val {
214    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
215        match self {
216            Val::Null       => 0u8.hash(state),
217            Val::Bool(b)    => { 1u8.hash(state); b.hash(state); }
218            Val::Int(n)     => { 2u8.hash(state); n.hash(state); }
219            Val::Float(f)   => { 2u8.hash(state); f.to_bits().hash(state); }
220            Val::Str(s)     => { 3u8.hash(state); s.hash(state); }
221            Val::Arr(a)     => { 4u8.hash(state); (Arc::as_ptr(a) as usize).hash(state); }
222            Val::Obj(m)     => { 5u8.hash(state); (Arc::as_ptr(m) as usize).hash(state); }
223        }
224    }
225}
226
227// ── Display ───────────────────────────────────────────────────────────────────
228
229impl std::fmt::Display for Val {
230    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231        match self {
232            Val::Null      => write!(f, "null"),
233            Val::Bool(b)   => write!(f, "{}", b),
234            Val::Int(n)    => write!(f, "{}", n),
235            Val::Float(fl) => write!(f, "{}", fl),
236            Val::Str(s)    => write!(f, "{}", s),
237            other => write!(f, "{}", serde_json::Value::from(other.clone())),
238        }
239    }
240}