Skip to main content

simple_update_in/
value.rs

1//! Dynamic JSON-like value model.
2//!
3//! The update engine walks a tree of maps, arrays, and scalars. This module
4//! defines that tree. It mirrors JavaScript value semantics closely enough to
5//! reproduce the engine's behavior:
6//!
7//! - `Undefined` is distinct from `Null`. `Undefined` marks an absent root, a
8//!   removal request, and a sparse array hole read back as missing.
9//! - Numbers follow `SameValue`. Every `NaN` equals every `NaN`, and `+0.0`
10//!   does not equal `-0.0`, matching `Object.is`.
11//! - Arrays and maps live behind [`Rc`] so callers can test structural sharing
12//!   with [`Value::ptr_eq`]. An unchanged subtree is returned by the same
13//!   pointer, never rebuilt.
14
15use std::rc::Rc;
16
17/// A map that keeps keys in insertion order.
18///
19/// Entries are stored once, as a flat list of pairs. Insertion order is
20/// intrinsic to that list. Predicate paths enumerate keys in this order.
21/// Equality ignores order, so two maps with the same pairs are equal whatever
22/// the order. The maps this library edits are small, so linear lookup is fine
23/// and matches how the engine clones whole maps along the path.
24#[derive(Clone, Debug, Default)]
25pub struct Map {
26    entries: Vec<(String, Value)>,
27}
28
29impl Map {
30    /// Create an empty map.
31    #[must_use]
32    pub fn new() -> Self {
33        Map {
34            entries: Vec::new(),
35        }
36    }
37
38    /// Whether the map has no keys.
39    #[must_use]
40    pub fn is_empty(&self) -> bool {
41        self.entries.is_empty()
42    }
43
44    /// Whether `key` is present.
45    #[must_use]
46    pub fn contains_key(&self, key: &str) -> bool {
47        self.entries.iter().any(|(k, _)| k == key)
48    }
49
50    /// Read the value at `key`.
51    #[must_use]
52    pub fn get(&self, key: &str) -> Option<&Value> {
53        self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
54    }
55
56    /// Insert or replace `key`. A new key is appended in insertion order. An
57    /// existing key keeps its position.
58    pub fn insert(&mut self, key: String, value: Value) {
59        if let Some(entry) = self.entries.iter_mut().find(|(k, _)| *k == key) {
60            entry.1 = value;
61        } else {
62            self.entries.push((key, value));
63        }
64    }
65
66    /// Remove `key` and its value, keeping the order of the rest.
67    pub fn remove(&mut self, key: &str) {
68        self.entries.retain(|(k, _)| k != key);
69    }
70
71    /// Iterate keys and values in insertion order.
72    pub fn iter(&self) -> impl Iterator<Item = (&String, &Value)> {
73        self.entries.iter().map(|(k, v)| (k, v))
74    }
75}
76
77impl PartialEq for Map {
78    /// Two maps are equal when they hold the same keys and values. Order is
79    /// ignored, matching deep value equality.
80    fn eq(&self, other: &Self) -> bool {
81        if self.entries.len() != other.entries.len() {
82            return false;
83        }
84        self.entries.iter().all(|(k, v)| other.get(k) == Some(v))
85    }
86}
87
88impl FromIterator<(String, Value)> for Map {
89    fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
90        let mut map = Map::new();
91        for (k, v) in iter {
92            map.insert(k, v);
93        }
94        map
95    }
96}
97
98impl IntoIterator for Map {
99    type Item = (String, Value);
100    type IntoIter = std::vec::IntoIter<(String, Value)>;
101
102    fn into_iter(self) -> Self::IntoIter {
103        self.entries.into_iter()
104    }
105}
106
107/// A JSON-like value.
108///
109/// Scalars are owned. `Array` and `Object` share their contents through [`Rc`]
110/// so an unchanged subtree can be returned by pointer.
111#[derive(Clone, Debug)]
112pub enum Value {
113    /// JavaScript `undefined`. Absent root, removal sentinel, or a read-back
114    /// sparse hole.
115    Undefined,
116    /// JavaScript `null`. A real value, treated as a map for type coercion.
117    Null,
118    /// A boolean.
119    Bool(bool),
120    /// A number. Carries the raw `f64` so `-0.0` and `NaN` survive.
121    Number(f64),
122    /// A string.
123    String(String),
124    /// An array. Sparse holes are stored as `Undefined`, since a JavaScript
125    /// hole reads back as `undefined`.
126    Array(Rc<Vec<Value>>),
127    /// A map of string keys to values.
128    Object(Rc<Map>),
129}
130
131impl Value {
132    /// Whether this is `Undefined`.
133    #[must_use]
134    pub fn is_undefined(&self) -> bool {
135        matches!(self, Value::Undefined)
136    }
137
138    /// Borrow the array contents, if this is an array.
139    #[must_use]
140    pub fn as_array(&self) -> Option<&[Value]> {
141        match self {
142            Value::Array(items) => Some(items),
143            _ => None,
144        }
145    }
146
147    /// Borrow the map contents, if this is an object.
148    #[must_use]
149    pub fn as_object(&self) -> Option<&Map> {
150        match self {
151            Value::Object(map) => Some(map),
152            _ => None,
153        }
154    }
155
156    /// Build an array value from items.
157    #[must_use]
158    pub fn array(items: Vec<Value>) -> Value {
159        Value::Array(Rc::new(items))
160    }
161
162    /// Build a map value from a [`Map`].
163    #[must_use]
164    pub fn object(map: Map) -> Value {
165        Value::Object(Rc::new(map))
166    }
167
168    /// Build a map value from key and value pairs, in order.
169    ///
170    /// Convenience for tests and call sites that hold literal data.
171    #[must_use]
172    pub fn from_pairs<K, I>(pairs: I) -> Value
173    where
174        K: Into<String>,
175        I: IntoIterator<Item = (K, Value)>,
176    {
177        let map = pairs.into_iter().map(|(k, v)| (k.into(), v)).collect();
178        Value::object(map)
179    }
180
181    /// Pointer equality for shared containers.
182    ///
183    /// Returns `true` when both values are the same array or the same map by
184    /// [`Rc`] pointer. This is the structural-sharing check: an untouched
185    /// subtree returns its original pointer, so callers can detect "no change"
186    /// cheaply. Scalars never share, so this returns `false` for them.
187    #[must_use]
188    pub fn ptr_eq(&self, other: &Value) -> bool {
189        match (self, other) {
190            (Value::Array(a), Value::Array(b)) => Rc::ptr_eq(a, b),
191            (Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b),
192            _ => false,
193        }
194    }
195
196    /// `SameValue` equality, the `Object.is` algorithm.
197    ///
198    /// Numbers follow `SameValue`: every `NaN` equals every `NaN` regardless of
199    /// bit pattern, and `+0.0` does not equal `-0.0`. Containers compare by
200    /// pointer, which is what the engine's no-op check needs. This is not deep
201    /// equality. Two distinct arrays with equal contents are not `SameValue`
202    /// equal.
203    #[must_use]
204    pub fn same_value(&self, other: &Value) -> bool {
205        match (self, other) {
206            (Value::Undefined, Value::Undefined) | (Value::Null, Value::Null) => true,
207            (Value::Bool(a), Value::Bool(b)) => a == b,
208            (Value::Number(a), Value::Number(b)) => {
209                (a.is_nan() && b.is_nan()) || a.to_bits() == b.to_bits()
210            }
211            (Value::String(a), Value::String(b)) => a == b,
212            (Value::Array(_), Value::Array(_)) | (Value::Object(_), Value::Object(_)) => {
213                self.ptr_eq(other)
214            }
215            _ => false,
216        }
217    }
218}
219
220impl From<f64> for Value {
221    fn from(n: f64) -> Value {
222        Value::Number(n)
223    }
224}
225
226impl From<f32> for Value {
227    fn from(n: f32) -> Value {
228        Value::Number(f64::from(n))
229    }
230}
231
232impl From<i32> for Value {
233    fn from(n: i32) -> Value {
234        Value::Number(f64::from(n))
235    }
236}
237
238impl From<u32> for Value {
239    fn from(n: u32) -> Value {
240        Value::Number(f64::from(n))
241    }
242}
243
244impl From<bool> for Value {
245    fn from(b: bool) -> Value {
246        Value::Bool(b)
247    }
248}
249
250impl From<&str> for Value {
251    fn from(s: &str) -> Value {
252        Value::String(s.to_string())
253    }
254}
255
256impl From<String> for Value {
257    fn from(s: String) -> Value {
258        Value::String(s)
259    }
260}
261
262impl From<Vec<Value>> for Value {
263    fn from(items: Vec<Value>) -> Value {
264        Value::array(items)
265    }
266}
267
268impl From<Map> for Value {
269    fn from(map: Map) -> Value {
270        Value::object(map)
271    }
272}
273
274impl PartialEq for Value {
275    /// Deep value equality.
276    ///
277    /// Numbers follow `SameValue`, so `NaN == NaN` holds for any two `NaN` and
278    /// tests can assert on it. This matches `same_value` on scalars and is
279    /// deliberate. Do not switch the `Number` arm to plain `==`, or `NaN`
280    /// assertions break. Arrays and maps compare element by element, which is
281    /// where this differs from `same_value`: two distinct arrays with equal
282    /// contents are equal here but not under `same_value`. This is the equality
283    /// `assert_eq!` uses in tests, not the engine's no-op check.
284    fn eq(&self, other: &Value) -> bool {
285        match (self, other) {
286            (Value::Undefined, Value::Undefined) | (Value::Null, Value::Null) => true,
287            (Value::Bool(a), Value::Bool(b)) => a == b,
288            (Value::Number(a), Value::Number(b)) => {
289                (a.is_nan() && b.is_nan()) || a.to_bits() == b.to_bits()
290            }
291            (Value::String(a), Value::String(b)) => a == b,
292            (Value::Array(a), Value::Array(b)) => a == b,
293            (Value::Object(a), Value::Object(b)) => a == b,
294            _ => false,
295        }
296    }
297}