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}