jtd/
lib.rs

1//! An implementation of [JSON Type Definition](https://jsontypedef.com), [RFC
2//! 8927](https://tools.ietf.org/html/rfc8927).
3//!
4//! `jtd` lets you parse and ensure the validity of JSON Typedef schemas, and
5//! then validate JSON data against those schemas. If your goal is instead to
6//! generate Rust types from JSON Typedef schemas, see
7//! [`jtd-codegen`](https://github.com/jsontypedef/json-typedef-codegen).
8//!
9//! # Quick start
10//!
11//! Here's how you can parse a JSON Typedef schema and then use it to validate
12//! data against that schema.
13//!
14//! ```
15//! use jtd::Schema;
16//! use serde_json::json;
17//!
18//! let schema = Schema::from_serde_schema(
19//!     serde_json::from_value(json!({
20//!         "properties": {
21//!             "foo": { "type": "string" },
22//!             "bar": { "type": "boolean" }
23//!         }
24//!     }))
25//!     .expect("Parse schema"),
26//! )
27//! .expect("Construct schema from JSON data");
28//!
29//! schema.validate().expect("Invalid schema");
30//!
31//! // This input is ok, so validate comes back empty.
32//! let input_ok = json!({ "foo": "xxx", "bar": true });
33//! assert!(jtd::validate(&schema, &input_ok, Default::default()).unwrap().is_empty());
34//!
35//! // This input is bad (bar has type string, not boolean), so validate does
36//! // not come back empty.
37//! let input_bad = json!({ "foo": "xxx", "bar": "false" });
38//! assert!(!jtd::validate(&schema, &input_bad, Default::default()).unwrap().is_empty());
39//! ```
40//!
41//! Or, at a high level:
42//!
43//! 1. Use `serde_json` to parse JSON data into a [`SerdeSchema`].
44//! 2. Convert that into a [`Schema`] using [`Schema::from_serde_schema`].
45//! 3. Optionally, ensure that schema is "valid" using [`Schema::validate`].
46//! 4. Verify data against that schema using [`validate()`].
47//!
48//! # Common usage
49//!
50//! The example above shows you how you can quickly use JSON Typedef to check
51//! whether data is valid. But in the real world, you usually want to know what
52//! the validation errors were, rather than just flatly rejecting input as
53//! "invalid" without any further details.
54//!
55//! One benefit of JSON Type Definition is that the exact data inside the
56//! validation errors is part of the specification; that means validation errors
57//! are portable. Here's an example of what those validation errors look like,
58//! and how you can access them with this crate.
59//!
60//! ```
61//! use jtd::{Schema, ValidationErrorIndicator};
62//! use serde_json::json;
63//!
64//! let schema = Schema::from_serde_schema(
65//!     serde_json::from_value(json!({
66//!         "properties": {
67//!             "name": { "type": "string" },
68//!             "age": { "type": "uint32" },
69//!             "phones": {
70//!                 "elements": {
71//!                     "type": "string"
72//!                 }
73//!             }
74//!         }
75//!     }))
76//!     .expect("Parse schema"),
77//! )
78//! .expect("Construct schema from JSON data");
79//!
80//! schema.validate().expect("Invalid schema");
81//!
82//! // Since this first example is valid, we'll get back an empty list of
83//! // validation errors.
84//! let input_ok = json!({
85//!     "name": "John Doe",
86//!     "age": 43,
87//!     "phones": ["+44 1234567", "+44 2345678"]
88//! });
89//!
90//! assert_eq!(
91//!     Vec::<ValidationErrorIndicator>::new(),
92//!     jtd::validate(&schema, &input_ok, Default::default()).unwrap(),
93//! );
94//!
95//! // This example is invalid, so we'll get back three validation errors:
96//! //
97//! // 1. "name" is required but not present,
98//! // 2. "age" has the wrong type
99//! // 3. "phones[1]" has the wrong type
100//! let input_bad = json!({
101//!     "age": "43",
102//!     "phones": ["+44 1234567", 442345678]
103//! });
104//!
105//! // Each error indicator has two pieces of information: the path to the part
106//! // of the input that was rejected (the "instance path"), and the part of the
107//! // schema that rejected it (the "schema path").
108//! //
109//! // The exact values of the instance path and schema path is specified in the
110//! // JSON Type Definition spec.
111//! assert_eq!(
112//!     vec![
113//!         // "age" has the wrong type (required by "/properties/age/type")
114//!         ValidationErrorIndicator {
115//!             instance_path: vec!["age".into()],
116//!             schema_path: vec!["properties".into(), "age".into(), "type".into()],
117//!         },
118//!
119//!         // "name" is missing (required by "/properties/name")
120//!         ValidationErrorIndicator {
121//!             instance_path: vec![],
122//!             schema_path: vec!["properties".into(), "name".into()],
123//!         },
124//!
125//!         // "phones/1" has the wrong type (required by "/properties/phones/elements/type")
126//!         ValidationErrorIndicator {
127//!             instance_path: vec!["phones".into(), "1".into()],
128//!             schema_path: vec![
129//!                 "properties".into(),
130//!                 "phones".into(),
131//!                 "elements".into(),
132//!                 "type".into()
133//!             ],
134//!         },
135//!     ],
136//!     jtd::validate(&schema, &input_bad, Default::default()).unwrap(),
137//! );
138//! ```
139//!
140//! # Advanced usage
141//!
142//! The examples above skim over some details of how you can use this crate.
143//! Here are pieces of documentation that you may find relevant:
144//!
145//! * If you want to convert JSON Type Defintion schemas to/from JSON, and
146//!   validate whether a schema is valid, see [`SerdeSchema`],
147//!   [`Schema::from_serde_schema`], and [`Schema::validate`].
148//!
149//! * If you want better performance out of [`validate()`], see
150//!   [`ValidateOptions`] to see how you can make validation faster.
151//!
152//! # Security considerations
153//!
154//! If you're running [`validate()`] with untrusted schemas (untrusted inputs is
155//! fine), then be aware of this security consideration from RFC 8927:
156//!
157//! > Implementations that evaluate user-inputted schemas SHOULD implement
158//! > mechanisms to detect and abort circular references that might cause a
159//! > naive implementation to go into an infinite loop.  Without such
160//! > mechanisms, implementations may be vulnerable to denial-of-service
161//! > attacks.
162//!
163//! This crate supports that "detect and abort" mechanism via
164//! [`ValidateOptions::with_max_depth`]. Please see that documentation if you're
165//! validating data against untrusted schemas.
166
167mod schema;
168mod serde_schema;
169mod validate;
170
171pub use schema::*;
172pub use serde_schema::*;
173pub use validate::*;