modality_utils/
json_stringify_deterministic.rs

1use 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              // In Rust, we can't directly call a method on a JSON object
41              // This is a placeholder for the JavaScript `obj.toJSON()` functionality
42              // You might want to implement a custom logic here
43              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    // Detect circular structure
55    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));  // Changed this line to handle null values
126                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}