bson/extjson/
json.rs

1use std::convert::{TryFrom, TryInto};
2
3use serde::de::{Error as _, Unexpected};
4use serde_json::{json, Value};
5
6use crate::{
7    error::{Error, Result},
8    extjson::models,
9    Binary,
10    Bson,
11    DbPointer,
12    Document,
13    JavaScriptCodeWithScope,
14    Regex,
15    Timestamp,
16};
17
18impl From<serde_json::Error> for Error {
19    fn from(error: serde_json::Error) -> Self {
20        Self::deserialization(error)
21    }
22}
23
24/// Converts the [`serde_json::Map`] into [`Bson`]. This conversion can interpret both canonical
25/// and relaxed [extended JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/).
26impl TryFrom<serde_json::Map<String, serde_json::Value>> for Bson {
27    type Error = Error;
28
29    fn try_from(obj: serde_json::Map<String, serde_json::Value>) -> Result<Self> {
30        if obj.contains_key("$oid") {
31            let oid: models::ObjectId = serde_json::from_value(obj.into())?;
32            return Ok(Bson::ObjectId(oid.parse()?));
33        }
34
35        if obj.contains_key("$symbol") {
36            let symbol: models::Symbol = serde_json::from_value(obj.into())?;
37            return Ok(Bson::Symbol(symbol.value));
38        }
39
40        if obj.contains_key("$regularExpression") {
41            let regex: models::Regex = serde_json::from_value(obj.into())?;
42            return Ok(regex.parse()?.into());
43        }
44
45        if obj.contains_key("$numberInt") {
46            let int: models::Int32 = serde_json::from_value(obj.into())?;
47            return Ok(Bson::Int32(int.parse()?));
48        }
49
50        if obj.contains_key("$numberLong") {
51            let int: models::Int64 = serde_json::from_value(obj.into())?;
52            return Ok(Bson::Int64(int.parse()?));
53        }
54
55        if obj.contains_key("$numberDouble") {
56            let double: models::Double = serde_json::from_value(obj.into())?;
57            return Ok(Bson::Double(double.parse()?));
58        }
59
60        if obj.contains_key("$numberDecimal") {
61            let decimal: models::Decimal128 = serde_json::from_value(obj.into())?;
62            return Ok(Bson::Decimal128(decimal.parse()?));
63        }
64
65        if obj.contains_key("$binary") {
66            let binary: models::Binary = serde_json::from_value(obj.into())?;
67            return Ok(Bson::Binary(binary.parse()?));
68        }
69
70        if obj.contains_key("$uuid") {
71            let uuid: models::Uuid = serde_json::from_value(obj.into())?;
72            return Ok(Bson::Binary(uuid.parse()?));
73        }
74
75        if obj.contains_key("$code") {
76            let code_w_scope: models::JavaScriptCodeWithScope = serde_json::from_value(obj.into())?;
77            return match code_w_scope.scope {
78                Some(scope) => Ok(crate::JavaScriptCodeWithScope {
79                    code: code_w_scope.code,
80                    scope: scope.try_into()?,
81                }
82                .into()),
83                None => Ok(Bson::JavaScriptCode(code_w_scope.code)),
84            };
85        }
86
87        if obj.contains_key("$timestamp") {
88            let ts: models::Timestamp = serde_json::from_value(obj.into())?;
89            return Ok(ts.parse().into());
90        }
91
92        if obj.contains_key("$date") {
93            let extjson_datetime: models::DateTime = serde_json::from_value(obj.into())?;
94            return Ok(Bson::DateTime(extjson_datetime.parse()?));
95        }
96
97        if obj.contains_key("$minKey") {
98            let min_key: models::MinKey = serde_json::from_value(obj.into())?;
99            return min_key.parse();
100        }
101
102        if obj.contains_key("$maxKey") {
103            let max_key: models::MaxKey = serde_json::from_value(obj.into())?;
104            return max_key.parse();
105        }
106
107        if obj.contains_key("$dbPointer") {
108            let db_ptr: models::DbPointer = serde_json::from_value(obj.into())?;
109            return Ok(db_ptr.parse()?.into());
110        }
111
112        if obj.contains_key("$undefined") {
113            let undefined: models::Undefined = serde_json::from_value(obj.into())?;
114            return undefined.parse();
115        }
116
117        Ok(Bson::Document(obj.try_into()?))
118    }
119}
120
121/// Converts the [`serde_json::Value`] into [`Bson`]. This conversion can interpret both canonical
122/// and relaxed [extended JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/).
123impl TryFrom<serde_json::Value> for Bson {
124    type Error = Error;
125
126    fn try_from(value: serde_json::Value) -> Result<Self> {
127        match value {
128            serde_json::Value::Number(x) => x
129                .as_i64()
130                .map(|i| {
131                    if i >= i32::MIN as i64 && i <= i32::MAX as i64 {
132                        Bson::Int32(i as i32)
133                    } else {
134                        Bson::Int64(i)
135                    }
136                })
137                .or_else(|| x.as_f64().map(Bson::from))
138                .ok_or_else(|| {
139                    Error::invalid_value(
140                        Unexpected::Other(format!("{}", x).as_str()),
141                        &"a number that could fit in i32, i64, or f64",
142                    )
143                }),
144            serde_json::Value::String(x) => Ok(x.into()),
145            serde_json::Value::Bool(x) => Ok(x.into()),
146            serde_json::Value::Array(x) => Ok(Bson::Array(
147                x.into_iter()
148                    .map(Bson::try_from)
149                    .collect::<Result<Vec<Bson>>>()?,
150            )),
151            serde_json::Value::Null => Ok(Bson::Null),
152            serde_json::Value::Object(map) => map.try_into(),
153        }
154    }
155}
156
157/// Converts the [`serde_json::Map`] into a [`Document`]. This conversion can interpret both
158/// canonical and relaxed [extended JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/).
159impl TryFrom<serde_json::Map<String, serde_json::Value>> for Document {
160    type Error = Error;
161
162    fn try_from(obj: serde_json::Map<String, serde_json::Value>) -> Result<Self> {
163        Ok(obj
164            .into_iter()
165            .map(|(k, v)| -> Result<(String, Bson)> {
166                let value: Bson = v.try_into()?;
167                Ok((k, value))
168            })
169            .collect::<Result<Vec<(String, Bson)>>>()?
170            .into_iter()
171            .collect())
172    }
173}
174
175/// Converts [`Bson`] into a [`serde_json::Value`] in relaxed
176/// [extended JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/).
177impl From<Bson> for Value {
178    fn from(bson: Bson) -> Self {
179        bson.into_relaxed_extjson()
180    }
181}
182
183impl Bson {
184    /// Converts this value into a [`serde_json::Value`] in relaxed
185    /// [extended JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/)
186    /// format.
187    pub fn into_relaxed_extjson(self) -> Value {
188        match self {
189            Bson::Double(v) if v.is_nan() => {
190                let s = if v.is_sign_negative() { "-NaN" } else { "NaN" };
191
192                json!({ "$numberDouble": s })
193            }
194            Bson::Double(v) if v.is_infinite() => {
195                let s = if v.is_sign_negative() {
196                    "-Infinity"
197                } else {
198                    "Infinity"
199                };
200
201                json!({ "$numberDouble": s })
202            }
203            Bson::Double(v) => json!(v),
204            Bson::String(v) => json!(v),
205            Bson::Array(v) => Value::Array(v.into_iter().map(Bson::into_relaxed_extjson).collect()),
206            Bson::Document(v) => Value::Object(
207                v.into_iter()
208                    .map(|(k, v)| (k, v.into_relaxed_extjson()))
209                    .collect(),
210            ),
211            Bson::Boolean(v) => json!(v),
212            Bson::Null => Value::Null,
213            Bson::RegularExpression(Regex { pattern, options }) => {
214                let mut chars: Vec<_> = options.as_str().chars().collect();
215                chars.sort_unstable();
216
217                let options: String = chars.into_iter().collect();
218
219                json!({
220                    "$regularExpression": {
221                        "pattern": pattern.into_string(),
222                        "options": options,
223                    }
224                })
225            }
226            Bson::JavaScriptCode(code) => json!({ "$code": code }),
227            Bson::JavaScriptCodeWithScope(JavaScriptCodeWithScope { code, scope }) => json!({
228                "$code": code,
229                "$scope": Bson::Document(scope).into_relaxed_extjson(),
230            }),
231            Bson::Int32(v) => v.into(),
232            Bson::Int64(v) => v.into(),
233            Bson::Timestamp(Timestamp { time, increment }) => json!({
234                "$timestamp": {
235                    "t": time,
236                    "i": increment,
237                }
238            }),
239            Bson::Binary(Binary { subtype, ref bytes }) => {
240                let tval: u8 = From::from(subtype);
241                json!({
242                    "$binary": {
243                        "base64": crate::base64::encode(bytes),
244                        "subType": hex::encode([tval]),
245                    }
246                })
247            }
248            Bson::ObjectId(v) => json!({"$oid": v.to_hex()}),
249            Bson::DateTime(v) if v.timestamp_millis() >= 0 && v.to_time_0_3().year() <= 9999 => {
250                json!({
251                    // Unwrap safety: timestamps in the guarded range can always be formatted.
252                    "$date": v.try_to_rfc3339_string().unwrap(),
253                })
254            }
255            Bson::DateTime(v) => json!({
256                "$date": { "$numberLong": v.timestamp_millis().to_string() },
257            }),
258            Bson::Symbol(v) => json!({ "$symbol": v }),
259            Bson::Decimal128(v) => json!({ "$numberDecimal": v.to_string() }),
260            Bson::Undefined => json!({ "$undefined": true }),
261            Bson::MinKey => json!({ "$minKey": 1 }),
262            Bson::MaxKey => json!({ "$maxKey": 1 }),
263            Bson::DbPointer(DbPointer {
264                ref namespace,
265                ref id,
266            }) => json!({
267                "$dbPointer": {
268                    "$ref": namespace,
269                    "$id": {
270                        "$oid": id.to_hex()
271                    }
272                }
273            }),
274        }
275    }
276
277    /// Converts this value into a [`serde_json::Value`] in canonical
278    /// [extended JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/)
279    /// format.
280    pub fn into_canonical_extjson(self) -> Value {
281        match self {
282            Bson::Int32(i) => json!({ "$numberInt": i.to_string() }),
283            Bson::Int64(i) => json!({ "$numberLong": i.to_string() }),
284            Bson::Double(f) if f.is_normal() => {
285                let mut s = f.to_string();
286                if f.fract() == 0.0 {
287                    s.push_str(".0");
288                }
289
290                json!({ "$numberDouble": s })
291            }
292            Bson::Double(f) if f == 0.0 => {
293                let s = if f.is_sign_negative() { "-0.0" } else { "0.0" };
294
295                json!({ "$numberDouble": s })
296            }
297            Bson::DateTime(date) => {
298                json!({ "$date": { "$numberLong": date.timestamp_millis().to_string() } })
299            }
300            Bson::Array(arr) => {
301                Value::Array(arr.into_iter().map(Bson::into_canonical_extjson).collect())
302            }
303            Bson::Document(arr) => Value::Object(
304                arr.into_iter()
305                    .map(|(k, v)| (k, v.into_canonical_extjson()))
306                    .collect(),
307            ),
308            Bson::JavaScriptCodeWithScope(JavaScriptCodeWithScope { code, scope }) => json!({
309                "$code": code,
310                "$scope": Bson::Document(scope).into_canonical_extjson(),
311            }),
312
313            other => other.into_relaxed_extjson(),
314        }
315    }
316}