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}