1use serde_json::{json, Map, Value};
21
22pub const KEY_OID: &str = "$oid";
23pub const KEY_DATE: &str = "$date";
24pub const KEY_DECIMAL: &str = "$numberDecimal";
25pub const KEY_LONG: &str = "$numberLong";
26pub const KEY_BINARY: &str = "$binary";
27
28pub fn object_id(hex: &str) -> Value {
30 json!({ KEY_OID: hex.to_ascii_lowercase() })
31}
32
33pub fn date(ms: i64) -> Value {
35 json!({ KEY_DATE: ms })
36}
37
38pub fn decimal128(s: &str) -> Value {
40 json!({ KEY_DECIMAL: s })
41}
42
43pub fn number_long(v: i64) -> Value {
45 json!({ KEY_LONG: v.to_string() })
46}
47
48pub fn as_object_id(v: &Value) -> Option<&str> {
50 v.as_object()?.get(KEY_OID)?.as_str()
51}
52
53pub fn as_date_ms(v: &Value) -> Option<i64> {
55 v.as_object()?.get(KEY_DATE)?.as_i64()
56}
57
58pub fn is_extended(v: &Value) -> bool {
60 match v.as_object() {
61 None => false,
62 Some(o) if o.len() != 1 => false,
63 Some(o) => {
64 let k = o.keys().next().map(String::as_str).unwrap_or("");
65 matches!(k, KEY_OID | KEY_DATE | KEY_DECIMAL | KEY_LONG | KEY_BINARY)
66 }
67 }
68}
69
70pub fn canonicalize(v: &Value) -> Value {
77 match v {
78 Value::Array(a) => Value::Array(a.iter().map(canonicalize).collect()),
79 Value::Object(o) => {
80 if is_extended(v) {
81 if let Some((k, inner)) = o.iter().next() {
83 return match (k.as_str(), inner) {
84 (KEY_OID, Value::String(s)) => object_id(s),
85 _ => {
86 let mut m = Map::new();
87 m.insert(k.clone(), inner.clone());
88 Value::Object(m)
89 }
90 };
91 }
92 }
93 Value::Object(
94 o.iter()
95 .map(|(k, x)| (k.clone(), canonicalize(x)))
96 .collect(),
97 )
98 }
99 other => other.clone(),
100 }
101}
102
103pub fn id_key(v: &Value) -> String {
108 match v {
109 Value::String(s) => s.clone(),
110 Value::Null => "null".to_string(),
111 Value::Bool(b) => b.to_string(),
112 Value::Number(n) => n.to_string(),
113 Value::Object(_) => as_object_id(v).map(String::from).unwrap_or_else(|| dump(v)),
114 Value::Array(_) => dump(v),
115 }
116}
117
118fn dump(v: &Value) -> String {
119 serde_json::to_string(v).unwrap_or_default()
120}