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
24impl 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
121impl 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
157impl 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
175impl From<Bson> for Value {
178 fn from(bson: Bson) -> Self {
179 bson.into_relaxed_extjson()
180 }
181}
182
183impl Bson {
184 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 "$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 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}