jsonutil/lib.rs
1//! Library to handle JSON schema and BSON schema types
2//!
3//! Document validation on MongoDB uses an hybrid form of
4//! JSON schema and BSON schema to implement runtime validation of
5//! data inserted/updated on a database collection.
6//!
7//! JSON schema is too limited to express all database native types
8//! which are thus expressed by [BSON types](https://www.mongodb.com/docs/manual/reference/bson-types/).
9//! MongoDB allows to define data schema and validation, when possible, as
10//! JSON type using the key `type` and enhance with the key `bsonType`.
11//! Thus there are 3 forms to read validations from MongoDB
12//!
13//! - [bson::BsonSchema] using only the `bsonType` key
14//! - [mongodb::MongodbJsonSchema] hybrid using both `type` and `bsonType` key
15//! - [json::JsonSchema] which is a partial view on previous methods and complies with Draft-04
16//!
17//! BSON types are mapped onto JSON types using the following table:
18//!
19//! | BSON Type | `bsonType` | `type` | JSON type |
20//! | --------------------- | ------------ | --------: | --------: |
21//! | Double | "double" | "number" | Number |
22//! | String | "string" | "string" | String |
23//! | Object | "object" | "object" | Object |
24//! | Array | "array" | "array" | Array |
25//! | Binary Data | "binData" | - | - |
26//! | ObjectId | "objectId" | - | - |
27//! | Boolean | "bool" | "boolean" | Boolean |
28//! | Date | "date" | - | - |
29//! | Null | "null" | "null" | Null |
30//! | Regular Expression | "regex" | - | - |
31//! | JavaScript | "javascript" | - | - |
32//! | 32-bit Signed Integer | "int" | "integer" | Integer |
33//! | 64-bit Signed Integer | "long" | "integer" | Integer |
34//! | Timestamp | "timestamp" | - | - |
35//! | 128-bit Decimal Float | "decimal" | "number" | Number |
36//! | Min Key | "minKey" | - | - |
37//! | Max Key | "maxKey" | - | - |
38
39use serde::{
40 Deserializer, Serializer,
41 de::{MapAccess, Visitor},
42 ser::SerializeMap,
43};
44use std::{collections::HashMap, fmt};
45
46pub mod bson;
47pub mod json;
48pub mod mongodb;
49
50fn deserialize_exists<'de, D>(deserializer: D) -> Result<(), D::Error>
51where
52 D: Deserializer<'de>,
53{
54 struct ExistsVisitor;
55
56 impl<'de> Visitor<'de> for ExistsVisitor {
57 type Value = ();
58
59 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
60 write!(formatter, "JsonSchema::Exists")
61 }
62
63 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
64 where
65 E: serde::de::Error,
66 {
67 if v {
68 Ok(())
69 } else {
70 Err(E::invalid_value(
71 serde::de::Unexpected::Bool(false),
72 &"a 'true'-valued boolean",
73 ))
74 }
75 }
76
77 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
78 where
79 A: MapAccess<'de>,
80 {
81 let mut object: HashMap<String, serde_json::Value> = HashMap::default();
82 while let Some((k, v)) = map.next_entry()? {
83 object.insert(k, v);
84 }
85
86 if object.is_empty() {
87 Ok(())
88 } else {
89 Err(<A::Error as serde::de::Error>::custom(
90 "Empty object expected",
91 ))
92 }
93 }
94 }
95
96 deserializer.deserialize_any(ExistsVisitor)
97}
98
99fn serialize_exists<S>(serializer: S) -> Result<S::Ok, S::Error>
100where
101 S: Serializer,
102{
103 let map = serializer.serialize_map(Some(0))?;
104 map.end()
105}
106
107fn check_array_unsorted_eq<T>(first: &[T], second: &[T]) -> bool
108where
109 T: PartialEq,
110{
111 first.len() == second.len() && first.iter().all(|next| second.contains(next))
112}