rsonschema/lib.rs
1#![deny(missing_docs)]
2#![deny(warnings)]
3
4//! # **rsonschema**
5//!
6//! A fast, simple, user-friendly JSON Schema validator for Rust
7//!
8//! ## Usage
9//!
10//! To use this crate, add it to your `Cargo.toml` running:
11//!
12//! ```bash
13//! cargo add rsonschema
14//! ```
15//!
16//! Then start using it in your code:
17//!
18//! ```rust
19//! let schema = serde_json::json!({
20//! "$schema": "https://json-schema.org/draft/2020-12/schema",
21//! "minLength": 3
22//! });
23//!
24//! let instance = serde_json::json!("foo");
25//! let report = rsonschema::validate(
26//! &instance,
27//! schema.clone(),
28//! );
29//! assert!(report.is_valid());
30//!
31//! let instance = serde_json::json!("a");
32//! let report = rsonschema::validate(
33//! &instance,
34//! schema,
35//! );
36//! assert_eq!(
37//! report,
38//! rsonschema::ValidationReport {
39//! errors: Some(
40//! rsonschema::error::ValidationErrors::from([
41//! rsonschema::error::ValidationError {
42//! instance: serde_json::json!("a"),
43//! type_: rsonschema::error::type_::ValidationErrorType::MinLength {
44//! limit: 3.into(),
45//! },
46//! ..Default::default()
47//! }
48//! ])
49//! ),
50//! ..Default::default()
51//! }
52//! );
53//! ```
54//!
55//! ## Contribute
56//!
57//! ### FAQ
58//!
59//! #### Too many arguments?
60//!
61//! We doubted whether having such a large number of arguments on `validate` method was a correct choice.
62//! Currently we suppose this is the best option because every argument has its own differnt lifetime:
63//!
64//! - `instance` is the reference to the JSON instance to be validated and it is borrowed from the caller.
65//! It changes for every validation subschema
66//! - `state` is a mutable reference to the state of the validation process.
67//! It contains attributes that dont't change during the validation process,
68//! such as the absolute schemas already encountered and the reference resolver
69//! - `relative_schemas` is a immutable reference to the relative schemas.
70//! It is fundamental for the `$ref` keyword.
71//! It is immutable because it is evaluated at the beginning the validation process for every new subschema
72//! - `parent_id` is a reference to the parent schema id
73//! It is fundamental for the `$ref` keyword.
74//! It is immutable because it changes at every validation subschema
75
76/// The module containing the error that may occurs while validating a JSON instance against a JSON schema
77pub mod error;
78/// The module containing the report of the validation
79mod report;
80/// The module containing the schema definitions
81pub mod schema;
82
83use either::Either;
84use serde_json::Value;
85use std::{collections, fmt};
86
87pub use report::ValidationReport;
88
89/// The custom reference resolver
90type RefResolver<'a> = &'a dyn Fn(&str) -> Option<Value>;
91
92/// The schemas involved in the validation
93type Schemas = collections::HashMap<schema::common::id::Id, Value>;
94
95/// Validate the given JSON instance against the schema
96pub fn validate(instance: &Value, schema: Value) -> ValidationReport {
97 validate_with_resolver(instance, schema, None, None)
98}
99
100/// Validate the given JSON instance against the schema using a custom reference resolver
101pub fn validate_with_resolver(
102 instance: &Value,
103 schema: Value,
104 pointer: Option<&str>,
105 ref_resolver: Option<RefResolver>,
106) -> ValidationReport {
107 let validable_schema = schema::common::ref_::get_schema(&schema, None, pointer);
108 match validable_schema {
109 Either::Left((validable_schema, parent_id)) => {
110 let parent_id = parent_id.as_ref();
111 let absolute_schemas = validable_schema.get_schemas(parent_id, true);
112 let mut relative_schemas = validable_schema.get_schemas(parent_id, false);
113 relative_schemas.insert(schema::common::id::Id::Relative(String::new()), schema);
114 let ref_resolver = ref_resolver.unwrap_or(&schema::common::ref_::resolve_ref);
115 let mut state = schema::common::state::State {
116 absolute_schemas,
117 ref_resolver,
118 };
119 validable_schema.validate(instance, &mut state, &relative_schemas, parent_id)
120 }
121 Either::Right(err) => ValidationReport {
122 errors: Some(vec![err]),
123 ..Default::default()
124 },
125 }
126}
127
128trait Validable: fmt::Debug {
129 fn get_schemas(&self, parent_id: Option<&schema::common::id::Id>, is_absolute: bool)
130 -> Schemas;
131
132 fn validate(
133 &self,
134 instance: &Value,
135 state: &mut schema::common::state::State,
136 relative_schemas: &Schemas,
137 parent_id: Option<&schema::common::id::Id>,
138 ) -> ValidationReport;
139}
140
141#[cfg(test)]
142mod tests {
143
144 use super::*;
145
146 pub(crate) fn assert_validate(
147 instance: Value,
148 schema: Value,
149 errors: Option<error::ValidationErrors>,
150 ) {
151 let actual = validate(&instance, schema);
152 let expected = ValidationReport {
153 errors,
154 ids: actual.ids.clone(),
155 evaluated: actual.evaluated.clone(),
156 };
157 assert_eq!(actual, expected);
158 }
159}