Skip to main content

merman_core/
compatibility_json.rs

1use crate::{OperationControl, OperationControlResult};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3#[cfg(any(
4    test,
5    feature = "diagram-mindmap",
6    feature = "diagram-state",
7    feature = "diagram-usecase"
8))]
9use serde_json::Number;
10use serde_json::{Map, Value};
11use std::collections::HashMap;
12use std::fmt;
13use std::io::Write;
14use std::mem::size_of;
15use std::ops::Deref;
16
17/// Mermaid-compatible JSON with iterative ownership and export.
18///
19/// Ordinary clone, equality, debug formatting, and disposal do not recurse through containers.
20/// Generic serde serialization supports at most 128 nested JSON containers; use
21/// [`Self::write_json`] for deeper output. Borrowed raw values and explicitly extracted unmanaged
22/// values retain serde_json's own lifecycle and serialization behavior.
23#[derive(Default)]
24pub struct ManagedSemanticJson {
25    value: Value,
26}
27
28impl ManagedSemanticJson {
29    /// Takes ownership of an existing JSON value without changing its shape or object order.
30    pub fn from_value(value: Value) -> Self {
31        Self { value }
32    }
33
34    /// Borrows the raw payload. Cloning or serializing this borrow bypasses managed guarantees.
35    pub fn as_value(&self) -> &Value {
36        &self.value
37    }
38
39    pub(crate) fn as_value_mut(&mut self) -> &mut Value {
40        &mut self.value
41    }
42
43    /// Transfers the payload into an unmanaged value.
44    ///
45    /// The caller assumes responsibility for deep clone, serialization, and disposal afterward.
46    pub fn into_unmanaged_value(mut self) -> Value {
47        std::mem::take(&mut self.value)
48    }
49
50    /// Clones while observing the operation control and safely disposing of partial clones.
51    pub fn clone_controlled(&self, control: &OperationControl) -> OperationControlResult<Self> {
52        clone_value_nonrecursive_with_control(&self.value, control).map(Self::from_value)
53    }
54
55    /// Writes compact JSON without a container-depth limit.
56    pub fn write_json(&self, writer: impl Write) -> serde_json::Result<()> {
57        self.write_json_controlled(writer, &OperationControl::new())
58            .expect("a private operation control cannot be cancelled")
59    }
60
61    /// Writes compact JSON while observing cancellation between traversal steps.
62    ///
63    /// An already observed cancellation retains its phase and precedes a later writer error.
64    /// Otherwise, writer errors remain distinct from cancellation requests. The caller owns any
65    /// partial output.
66    pub fn write_json_controlled(
67        &self,
68        writer: impl Write,
69        control: &OperationControl,
70    ) -> OperationControlResult<serde_json::Result<()>> {
71        self.write_with_formatter(writer, control, serde_json::ser::CompactFormatter)
72    }
73
74    /// Writes indented JSON with serde_json's standard whitespace and no container-depth limit.
75    pub fn write_json_pretty(&self, writer: impl Write) -> serde_json::Result<()> {
76        self.write_json_pretty_controlled(writer, &OperationControl::new())
77            .expect("a private operation control cannot be cancelled")
78    }
79
80    /// Writes indented JSON while observing the original operation control.
81    pub fn write_json_pretty_controlled(
82        &self,
83        writer: impl Write,
84        control: &OperationControl,
85    ) -> OperationControlResult<serde_json::Result<()>> {
86        self.write_with_formatter(writer, control, serde_json::ser::PrettyFormatter::new())
87    }
88
89    fn write_with_formatter(
90        &self,
91        mut writer: impl Write,
92        control: &OperationControl,
93        mut formatter: impl serde_json::ser::Formatter,
94    ) -> OperationControlResult<serde_json::Result<()>> {
95        enum Task<'a> {
96            Value(&'a Value),
97            Key(&'a str),
98            ArrayItems(std::slice::Iter<'a, Value>, bool),
99            ObjectEntries(serde_json::map::Iter<'a>, bool),
100            ArrayValue(bool),
101            ArrayValueEnd,
102            ArrayEnd,
103            ObjectKey(bool),
104            ObjectValue,
105            ObjectValueEnd,
106            ObjectEnd,
107        }
108
109        let mut stack = vec![Task::Value(&self.value)];
110        let mut steps = 0usize;
111        while let Some(task) = stack.pop() {
112            if steps.is_multiple_of(64) {
113                control.checkpoint()?;
114            }
115            steps = steps.saturating_add(1);
116            let result = match task {
117                Task::ArrayItems(mut items, first) => {
118                    if let Some(item) = items.next() {
119                        stack.push(Task::ArrayItems(items, false));
120                        stack.push(Task::ArrayValueEnd);
121                        stack.push(Task::Value(item));
122                        stack.push(Task::ArrayValue(first));
123                    }
124                    Ok(())
125                }
126                Task::ObjectEntries(mut entries, first) => {
127                    if let Some((key, value)) = entries.next() {
128                        stack.push(Task::ObjectEntries(entries, false));
129                        stack.push(Task::ObjectValueEnd);
130                        stack.push(Task::Value(value));
131                        stack.push(Task::ObjectValue);
132                        stack.push(Task::Key(key));
133                        stack.push(Task::ObjectKey(first));
134                    }
135                    Ok(())
136                }
137                Task::ArrayValue(first) => formatter
138                    .begin_array_value(&mut writer, first)
139                    .map_err(serde_json::Error::io),
140                Task::ArrayValueEnd => formatter
141                    .end_array_value(&mut writer)
142                    .map_err(serde_json::Error::io),
143                Task::ArrayEnd => formatter
144                    .end_array(&mut writer)
145                    .map_err(serde_json::Error::io),
146                Task::ObjectKey(first) => formatter
147                    .begin_object_key(&mut writer, first)
148                    .map_err(serde_json::Error::io),
149                Task::ObjectValue => formatter
150                    .begin_object_value(&mut writer)
151                    .map_err(serde_json::Error::io),
152                Task::ObjectValueEnd => formatter
153                    .end_object_value(&mut writer)
154                    .map_err(serde_json::Error::io),
155                Task::ObjectEnd => formatter
156                    .end_object(&mut writer)
157                    .map_err(serde_json::Error::io),
158                Task::Key(key) => serde_json::to_writer(&mut writer, key),
159                Task::Value(Value::Array(items)) => {
160                    stack.push(Task::ArrayEnd);
161                    stack.push(Task::ArrayItems(items.iter(), true));
162                    formatter
163                        .begin_array(&mut writer)
164                        .map_err(serde_json::Error::io)
165                }
166                Task::Value(Value::Object(entries)) => {
167                    stack.push(Task::ObjectEnd);
168                    stack.push(Task::ObjectEntries(entries.iter(), true));
169                    formatter
170                        .begin_object(&mut writer)
171                        .map_err(serde_json::Error::io)
172                }
173                Task::Value(scalar) => serde_json::to_writer(&mut writer, scalar),
174            };
175            if let Err(error) = result {
176                if let Some(cancelled) = control.observed_cancellation() {
177                    return Err(cancelled);
178                }
179                return Ok(Err(error));
180            }
181        }
182        control.checkpoint()?;
183        Ok(Ok(()))
184    }
185}
186
187impl From<Value> for ManagedSemanticJson {
188    fn from(value: Value) -> Self {
189        Self::from_value(value)
190    }
191}
192
193impl From<&Value> for ManagedSemanticJson {
194    fn from(value: &Value) -> Self {
195        Self::from_value(clone_value_nonrecursive(value))
196    }
197}
198
199impl Deref for ManagedSemanticJson {
200    type Target = Value;
201
202    fn deref(&self) -> &Self::Target {
203        self.as_value()
204    }
205}
206
207impl AsRef<Value> for ManagedSemanticJson {
208    fn as_ref(&self) -> &Value {
209        self.as_value()
210    }
211}
212
213impl Clone for ManagedSemanticJson {
214    fn clone(&self) -> Self {
215        Self::from(&self.value)
216    }
217}
218
219impl Drop for ManagedSemanticJson {
220    fn drop(&mut self) {
221        drop_value_nonrecursive(std::mem::take(&mut self.value));
222    }
223}
224
225pub(crate) fn equal_values(left: &Value, right: &Value) -> bool {
226    let mut stack = vec![(left, right)];
227    while let Some((left, right)) = stack.pop() {
228        match (left, right) {
229            (Value::Array(left), Value::Array(right)) if left.len() == right.len() => {
230                stack.extend(left.iter().zip(right));
231            }
232            (Value::Object(left), Value::Object(right)) if left.len() == right.len() => {
233                for (key, value) in left {
234                    let Some(other) = right.get(key) else {
235                        return false;
236                    };
237                    stack.push((value, other));
238                }
239            }
240            (Value::Null, Value::Null) => {}
241            (Value::Bool(left), Value::Bool(right)) if left == right => {}
242            (Value::Number(left), Value::Number(right)) if left == right => {}
243            (Value::String(left), Value::String(right)) if left == right => {}
244            _ => return false,
245        }
246    }
247    true
248}
249
250impl PartialEq for ManagedSemanticJson {
251    fn eq(&self, other: &Self) -> bool {
252        equal_values(&self.value, &other.value)
253    }
254}
255
256impl Eq for ManagedSemanticJson {}
257
258impl PartialEq<Value> for ManagedSemanticJson {
259    fn eq(&self, other: &Value) -> bool {
260        equal_values(&self.value, other)
261    }
262}
263
264impl PartialEq<ManagedSemanticJson> for Value {
265    fn eq(&self, other: &ManagedSemanticJson) -> bool {
266        equal_values(self, &other.value)
267    }
268}
269
270impl fmt::Debug for ManagedSemanticJson {
271    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
272        let mut bytes = Vec::new();
273        self.write_json(&mut bytes).map_err(|_| fmt::Error)?;
274        formatter.write_str(std::str::from_utf8(&bytes).map_err(|_| fmt::Error)?)
275    }
276}
277
278impl Serialize for ManagedSemanticJson {
279    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
280        let mut stack = vec![(&self.value, 0usize)];
281        while let Some((value, depth)) = stack.pop() {
282            if !matches!(value, Value::Array(_) | Value::Object(_)) {
283                continue;
284            }
285            let container_depth = depth.saturating_add(1);
286            if container_depth > 128 {
287                return Err(serde::ser::Error::custom(
288                    "semantic JSON exceeds generic serde's 128-container depth; use write_json",
289                ));
290            }
291            match value {
292                Value::Array(items) => {
293                    stack.extend(
294                        items
295                            .iter()
296                            .filter(|child| matches!(child, Value::Array(_) | Value::Object(_)))
297                            .map(|child| (child, container_depth)),
298                    );
299                }
300                Value::Object(entries) => {
301                    stack.extend(
302                        entries
303                            .values()
304                            .filter(|child| matches!(child, Value::Array(_) | Value::Object(_)))
305                            .map(|child| (child, container_depth)),
306                    );
307                }
308                _ => unreachable!("container kind was checked above"),
309            }
310        }
311        self.value.serialize(serializer)
312    }
313}
314
315impl<'de> Deserialize<'de> for ManagedSemanticJson {
316    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
317        Value::deserialize(deserializer).map(Self::from_value)
318    }
319}
320
321#[cfg(any(
322    test,
323    feature = "diagram-mindmap",
324    feature = "diagram-state",
325    feature = "diagram-usecase"
326))]
327pub(crate) fn number_value(value: f64) -> Value {
328    if value.is_finite()
329        && value.fract() == 0.0
330        && value >= i64::MIN as f64
331        && value < i64::MAX as f64
332    {
333        Value::Number(Number::from(value as i64))
334    } else {
335        Number::from_f64(value)
336            .map(Value::Number)
337            .unwrap_or(Value::Null)
338    }
339}
340
341pub(crate) fn clone_value_nonrecursive(value: &Value) -> Value {
342    let control = OperationControl::new();
343    clone_value_nonrecursive_with_control(value, &control)
344        .expect("a private operation control cannot be cancelled")
345}
346
347pub(crate) fn clone_value_nonrecursive_with_control(
348    value: &Value,
349    control: &OperationControl,
350) -> OperationControlResult<Value> {
351    clone_value_nonrecursive_controlled(value, usize::MAX, usize::MAX, control)
352        .map(|value| value.expect("unbounded config cloning cannot exceed its budget"))
353}
354
355pub(crate) fn clone_value_nonrecursive_controlled(
356    value: &Value,
357    max_retained_bytes: usize,
358    max_nesting_depth: usize,
359    control: &OperationControl,
360) -> OperationControlResult<Option<Value>> {
361    let mut cloned: HashMap<*const Value, Value> = HashMap::new();
362    let mut stack = vec![(value, false, 0usize)];
363    let mut retained_bytes = 0usize;
364    let mut visited_nodes = 0usize;
365
366    while let Some((current, visited, depth)) = stack.pop() {
367        if visited_nodes.is_multiple_of(64)
368            && let Err(cancelled) = control.checkpoint()
369        {
370            drop_cloned_values(cloned);
371            return Err(cancelled);
372        }
373        visited_nodes = visited_nodes.saturating_add(1);
374        let current_ptr = std::ptr::from_ref(current);
375        if visited {
376            retained_bytes = retained_bytes.saturating_add(value_clone_weight(current));
377            if retained_bytes > max_retained_bytes {
378                drop_cloned_values(cloned);
379                return Ok(None);
380            }
381            let value = match current {
382                Value::Null => Value::Null,
383                Value::Bool(v) => Value::Bool(*v),
384                Value::Number(v) => Value::Number(v.clone()),
385                Value::String(v) => Value::String(v.clone()),
386                Value::Array(items) => {
387                    let mut out = Vec::with_capacity(items.len());
388                    for (index, item) in items.iter().enumerate() {
389                        if index.is_multiple_of(64)
390                            && let Err(cancelled) = control.checkpoint()
391                        {
392                            drop_value_nonrecursive(Value::Array(out));
393                            drop_cloned_values(cloned);
394                            return Err(cancelled);
395                        }
396                        if let Some(value) = cloned.remove(&std::ptr::from_ref(item)) {
397                            out.push(value);
398                        }
399                    }
400                    Value::Array(out)
401                }
402                Value::Object(entries) => {
403                    let mut out = Map::new();
404                    for (index, (key, child)) in entries.iter().enumerate() {
405                        if index.is_multiple_of(64)
406                            && let Err(cancelled) = control.checkpoint()
407                        {
408                            drop_value_nonrecursive(Value::Object(out));
409                            drop_cloned_values(cloned);
410                            return Err(cancelled);
411                        }
412                        if let Some(value) = cloned.remove(&std::ptr::from_ref(child)) {
413                            out.insert(key.clone(), value);
414                        }
415                    }
416                    Value::Object(out)
417                }
418            };
419            cloned.insert(current_ptr, value);
420        } else {
421            let has_children = matches!(current, Value::Array(items) if !items.is_empty())
422                || matches!(current, Value::Object(entries) if !entries.is_empty());
423            if has_children && depth >= max_nesting_depth {
424                drop_cloned_values(cloned);
425                return Ok(None);
426            }
427            let structural_weight = value_structural_weight(current);
428            retained_bytes = retained_bytes.saturating_add(structural_weight);
429            if retained_bytes > max_retained_bytes {
430                drop_cloned_values(cloned);
431                return Ok(None);
432            }
433            stack.push((current, true, depth));
434            match current {
435                Value::Array(items) => {
436                    for item in items.iter().rev() {
437                        stack.push((item, false, depth.saturating_add(1)));
438                    }
439                }
440                Value::Object(entries) => {
441                    for child in entries.values().rev() {
442                        stack.push((child, false, depth.saturating_add(1)));
443                    }
444                }
445                Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {}
446            }
447        }
448    }
449
450    if let Err(cancelled) = control.checkpoint() {
451        drop_cloned_values(cloned);
452        return Err(cancelled);
453    }
454    Ok(Some(
455        cloned
456            .remove(&std::ptr::from_ref(value))
457            .unwrap_or(Value::Null),
458    ))
459}
460
461fn value_structural_weight(value: &Value) -> usize {
462    match value {
463        Value::Array(items) => items.len().saturating_mul(size_of::<Value>()),
464        Value::Object(entries) => entries.iter().fold(
465            entries.len().saturating_mul(
466                size_of::<String>()
467                    .saturating_add(size_of::<Value>())
468                    .saturating_add(size_of::<usize>().saturating_mul(4)),
469            ),
470            |weight, (key, _)| weight.saturating_add(key.len()),
471        ),
472        Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => 0,
473    }
474}
475
476fn value_clone_weight(value: &Value) -> usize {
477    size_of::<Value>().saturating_add(match value {
478        Value::String(value) => value.len(),
479        Value::Null | Value::Bool(_) | Value::Number(_) | Value::Array(_) | Value::Object(_) => 0,
480    })
481}
482
483#[cfg(test)]
484pub(crate) fn estimated_value_owned_heap_bytes(value: &Value) -> usize {
485    let mut retained_bytes = 0usize;
486    let mut stack = vec![value];
487    while let Some(current) = stack.pop() {
488        retained_bytes = retained_bytes
489            .saturating_add(value_structural_weight(current))
490            .saturating_add(value_clone_weight(current));
491        match current {
492            Value::Array(items) => stack.extend(items),
493            Value::Object(entries) => stack.extend(entries.values()),
494            Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {}
495        }
496    }
497    retained_bytes
498}
499
500fn drop_cloned_values(cloned: HashMap<*const Value, Value>) {
501    for value in cloned.into_values() {
502        drop_value_nonrecursive(value);
503    }
504}
505
506pub(crate) fn drop_value_nonrecursive(value: Value) {
507    let mut stack = vec![value];
508    while let Some(value) = stack.pop() {
509        match value {
510            Value::Array(items) => {
511                stack.extend(items);
512            }
513            Value::Object(entries) => {
514                stack.extend(entries.into_values());
515            }
516            Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {}
517        }
518    }
519}
520
521#[cfg(test)]
522mod tests {
523    use super::*;
524
525    #[test]
526    fn number_projection_does_not_saturate_at_the_rounded_i64_upper_bound() {
527        let upper_bound = i64::MAX as f64;
528        let projected = number_value(upper_bound);
529        assert_eq!(projected.as_f64(), Some(upper_bound));
530        assert_ne!(projected, Value::Number(Number::from(i64::MAX)));
531        assert_eq!(
532            number_value(i64::MIN as f64),
533            Value::Number(Number::from(i64::MIN))
534        );
535        assert_eq!(number_value(-0.0), Value::Number(Number::from(0)));
536        assert_eq!(number_value(f64::INFINITY), Value::Null);
537    }
538}