supercode_interchange/orchestration/codec/
canonical.rs1use serde_json::{Map, Value};
7
8pub fn sort_keys(value: &Value) -> Value {
10 match value {
11 Value::Array(items) => Value::Array(items.iter().map(sort_keys).collect()),
12 Value::Object(object) => {
13 let mut keys: Vec<&String> = object.keys().collect();
14 keys.sort();
15 let mut out = Map::new();
16 for key in keys {
17 out.insert(key.clone(), sort_keys(&object[key]));
18 }
19 Value::Object(out)
20 }
21 other => other.clone(),
22 }
23}
24
25pub fn canonical_json(value: &Value) -> String {
27 let sorted = sort_keys(value);
28 let mut text = pretty(&sorted, 0);
29 text.push('\n');
30 text
31}
32
33fn pretty(value: &Value, depth: usize) -> String {
35 let pad = |d: usize| " ".repeat(d);
36 match value {
37 Value::Array(items) if items.is_empty() => "[]".into(),
38 Value::Array(items) => {
39 let inner: Vec<String> = items
40 .iter()
41 .map(|v| format!("{}{}", pad(depth + 1), pretty(v, depth + 1)))
42 .collect();
43 format!("[\n{}\n{}]", inner.join(",\n"), pad(depth))
44 }
45 Value::Object(object) if object.is_empty() => "{}".into(),
46 Value::Object(object) => {
47 let mut keys: Vec<&String> = object.keys().collect();
48 keys.sort();
49 let inner: Vec<String> = keys
50 .into_iter()
51 .map(|k| {
52 format!(
53 "{}{}: {}",
54 pad(depth + 1),
55 serde_json::to_string(k).unwrap(),
56 pretty(&object[k], depth + 1)
57 )
58 })
59 .collect();
60 format!("{{\n{}\n{}}}", inner.join(",\n"), pad(depth))
61 }
62 Value::Number(n) => json_number(n),
63 other => serde_json::to_string(other).unwrap(),
64 }
65}
66
67fn json_number(n: &serde_json::Number) -> String {
70 if let Some(f) = n.as_f64() {
71 if n.is_f64() && f.fract() == 0.0 && f.abs() < 1e21 {
72 return format!("{}", f as i64);
73 }
74 }
75 n.to_string()
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn sorted_pretty_with_js_numbers() {
84 let v = serde_json::json!({"b": [1, 2.0, 2.5], "a": {"z": null, "y": "s"}});
85 assert_eq!(canonical_json(&v), "{\n \"a\": {\n \"y\": \"s\",\n \"z\": null\n },\n \"b\": [\n 1,\n 2,\n 2.5\n ]\n}\n");
86 assert_eq!(canonical_json(&serde_json::json!({})), "{}\n");
87 }
88}