modality_utils/
json_stringify_deterministic.rs1use serde_json::{Value, Map};
2use regex::Regex;
3
4#[derive(Clone)]
5pub struct Options {
6 space: Option<String>,
7 cycles: bool,
8 replacer: Option<fn(&str, &Value) -> Option<Value>>,
9 stringify: fn(&Value) -> String,
10 compare: Option<fn(&str, &Value, &str, &Value) -> std::cmp::Ordering>,
11}
12
13impl Default for Options {
14 fn default() -> Self {
15 Options {
16 space: None,
17 cycles: true,
18 replacer: None,
19 stringify: |v| serde_json::to_string(v).unwrap(),
20 compare: None,
21 }
22 }
23}
24
25pub fn serialize(value: &Value) -> Value {
26 match value {
27 Value::Null => Value::Null,
28 Value::Bool(_) => value.clone(),
29 Value::Number(_) => value.clone(),
30 Value::String(s) => {
31 if let Ok(re) = Regex::new(s) {
32 Value::String(re.to_string())
33 } else {
34 value.clone()
35 }
36 },
37 Value::Array(arr) => Value::Array(arr.iter().map(serialize).collect()),
38 Value::Object(obj) => {
39 if obj.contains_key("toJSON") {
40 value.clone()
44 } else {
45 Value::Object(obj.iter().map(|(k, v)| (k.clone(), serialize(v))).collect())
46 }
47 },
48 }
49}
50
51pub fn stringify_deterministic(obj: &Value, opts: Option<Options>) -> String {
52 let opts = opts.unwrap_or_default();
53
54 if !opts.cycles {
56 (opts.stringify)(obj);
57 }
58
59 let mut seen = Vec::new();
60
61 fn deterministic(
62 _parent: &Value,
63 key: &str,
64 node: &Value,
65 level: usize,
66 opts: &Options,
67 seen: &mut Vec<*const Value>,
68 ) -> Option<String> {
69 let indent = opts.space.as_ref().map(|s| "\n".to_string() + &s.repeat(level + 1)).unwrap_or_default();
70 let colon_separator = if opts.space.is_some() { ": " } else { ":" };
71
72 let mut node = serialize(node);
73 if let Some(replacer) = opts.replacer {
74 node = replacer(key, &node).unwrap_or(node);
75 }
76
77 if node == Value::Null {
78 return None;
79 }
80
81 match &node {
82 Value::Null => Some((opts.stringify)(&node)),
83 Value::Bool(_) | Value::Number(_) | Value::String(_) => Some((opts.stringify)(&node)),
84 Value::Array(arr) => {
85 let mut out = Vec::new();
86 for (i, item) in arr.iter().enumerate() {
87 let value = deterministic(
88 &node,
89 &i.to_string(),
90 item,
91 level + 1,
92 opts,
93 seen,
94 ).unwrap_or_else(|| (opts.stringify)(&Value::Null));
95 out.push(format!("{}{}{}", indent, opts.space.as_ref().unwrap_or(&"".to_string()), value));
96 }
97 Some(format!("[{}{}]", out.join(","), indent))
98 },
99 Value::Object(obj) => {
100 if opts.cycles {
101 let node_ptr = &node as *const Value;
102 if seen.contains(&node_ptr) {
103 return Some((opts.stringify)(&Value::String("[Circular]".to_string())));
104 } else {
105 seen.push(node_ptr);
106 }
107 }
108
109 let mut node_keys: Vec<&String> = obj.keys().collect();
110 if let Some(compare) = &opts.compare {
111 node_keys.sort_by(|a, b| compare(a, obj.get(*a).unwrap(), b, obj.get(*b).unwrap()));
112 } else {
113 node_keys.sort();
114 }
115
116 let mut out = Vec::new();
117 for key in node_keys {
118 let value = deterministic(
119 &node,
120 key,
121 obj.get(key).unwrap(),
122 level + 1,
123 opts,
124 seen,
125 ).unwrap_or_else(|| (opts.stringify)(&Value::Null)); let key_value = format!("{}{}{}", (opts.stringify)(&Value::String(key.to_string())), colon_separator, value);
127 out.push(format!("{}{}{}", indent, opts.space.as_ref().unwrap_or(&"".to_string()), key_value));
128 }
129
130 if opts.cycles {
131 seen.pop();
132 }
133
134 Some(format!("{{{}{}}}", out.join(","), indent))
135 },
136 }
137 }
138
139 deterministic(
140 &Value::Object(Map::new()),
141 "",
142 obj,
143 0,
144 &opts,
145 &mut seen,
146 ).unwrap_or_default()
147}