serde_json_schema/
lib.rs

1//! This crates provides simply the `Schema` struct. It resembles the latest (draft-07) [json-schema core spec](https://json-schema.org/latest/json-schema-core.html).
2//! If this spec is no longer up-to-date by the time you read this, please open a [new issue](https://github.com/hoodie/serde-json-schema/issues/new).
3//!
4//! If this type seems a bit confusing, then it's because json-schema is a bit too flexible.
5//!
6//! ## Usage
7//!
8//! ```
9//! use serde_json_schema::Schema;
10//! # use std::convert::TryFrom;
11//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
12//! # use std::fs;
13//! let schema_file = fs::read_to_string("./examples/address.schema.json")?;
14//! let address_schema = Schema::try_from(schema_file)?;
15//! # Ok(())
16//! # }
17//! ```
18
19#![deny(
20    missing_copy_implementations,
21    trivial_casts,
22    trivial_numeric_casts,
23    unsafe_code,
24    unused_import_braces,
25    unused_qualifications
26)]
27// #![warn(missing_docs)]
28
29use serde::{Deserialize, Serialize};
30pub use url::Url;
31
32use std::collections::HashMap;
33pub use std::convert::TryFrom;
34
35pub mod error;
36pub mod id;
37pub mod property;
38mod validation;
39
40use crate::error::Result;
41use crate::id::*;
42use crate::property::*;
43
44/// Represents a full JSON Schema Document
45// TODO: root array vs object
46#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
47pub struct Schema(SchemaInner);
48
49/// Represents a full JSON Schema Document
50// TODO: root array vs object
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
52#[serde(untagged)]
53enum SchemaInner {
54    /// The Common case
55    Schema(SchemaDefinition),
56    /// For Some stupid reason specs can just be `true` or `false`
57    Boolean(bool),
58}
59
60impl Schema {
61    pub fn draft_version(&self) -> Option<&str> {
62        match &self.0 {
63            SchemaInner::Schema(SchemaDefinition {
64                schema: Some(schema),
65                ..
66            }) => schema
67                .path_segments()
68                .and_then(|mut segments| segments.next()),
69            _ => None,
70        }
71    }
72
73    fn as_definition(&self) -> Option<&SchemaDefinition> {
74        match &self.0 {
75            SchemaInner::Schema(definition @ SchemaDefinition { .. }) => Some(definition),
76            _ => None,
77        }
78    }
79
80    pub fn id(&self) -> Option<&SchemaId> {
81        self.as_definition().and_then(|d| d.id.as_ref())
82    }
83
84    pub fn schema(&self) -> Option<&Url> {
85        self.as_definition().and_then(|d| d.schema.as_ref())
86    }
87
88    pub fn description(&self) -> Option<&str> {
89        self.as_definition().and_then(|d| d.description.as_deref())
90    }
91
92    pub fn specification(&self) -> Option<&PropertyInstance> {
93        match &self.0 {
94            SchemaInner::Schema(SchemaDefinition {
95                specification:
96                    Some(Property::Value(specification @ PropertyInstance::Object { .. })),
97                ..
98            }) => Some(specification),
99            SchemaInner::Schema(SchemaDefinition {
100                specification: Some(Property::Value(specification @ PropertyInstance::Array { .. })),
101                ..
102            }) => Some(specification),
103            _ => None,
104        }
105    }
106
107    pub fn properties(&self) -> Option<&HashMap<String, Property>> {
108        match self.specification() {
109            Some(PropertyInstance::Object { properties, .. }) => Some(properties),
110            _ => None,
111        }
112    }
113
114    pub fn required_properties(&self) -> Option<&Vec<String>> {
115        match self.specification() {
116            Some(PropertyInstance::Object { required, .. }) => required.as_ref(),
117            _ => None,
118        }
119    }
120
121    pub fn as_null(&self) -> Option<&PropertyInstance> {
122        match self.specification() {
123            Some(null @ PropertyInstance::Null) => Some(null),
124            _ => None,
125        }
126    }
127
128    pub fn as_boolean(&self) -> Option<&PropertyInstance> {
129        match self.specification() {
130            Some(boolean @ PropertyInstance::Boolean) => Some(boolean),
131            _ => None,
132        }
133    }
134
135    pub fn as_integer(&self) -> Option<&PropertyInstance> {
136        match self.specification() {
137            Some(integer @ PropertyInstance::Integer { .. }) => Some(integer),
138            _ => None,
139        }
140    }
141
142    pub fn as_object(&self) -> Option<&PropertyInstance> {
143        match self.specification() {
144            Some(object @ PropertyInstance::Object { .. }) => Some(object),
145            _ => None,
146        }
147    }
148
149    pub fn as_array(&self) -> Option<&PropertyInstance> {
150        match self.specification() {
151            Some(array @ PropertyInstance::Array { .. }) => Some(array),
152            _ => None,
153        }
154    }
155
156    pub fn as_number(&self) -> Option<&PropertyInstance> {
157        match self.specification() {
158            Some(number @ PropertyInstance::Number { .. }) => Some(number),
159            _ => None,
160        }
161    }
162
163    pub fn validate(&self, json: &serde_json::Value) -> std::result::Result<(), Vec<String>> {
164        match self.0 {
165            SchemaInner::Schema(SchemaDefinition {
166                specification: Some(Property::Value(ref prop)),
167                ..
168            }) => prop.validate(json),
169            SchemaInner::Schema(SchemaDefinition {
170                specification: Some(Property::Ref(_)),
171                ..
172            }) => unimplemented!(),
173            SchemaInner::Boolean(true) => {
174                eprintln!(r#"your schema is just "true", everything goes"#);
175                Ok(())
176            }
177            SchemaInner::Boolean(false) => Err(vec![String::from(
178                r#""the scheme "false" will never validate"#,
179            )]),
180            _ => Ok(()),
181        }
182    }
183}
184
185impl TryFrom<serde_json::Value> for Schema {
186    type Error = crate::error::Error;
187    fn try_from(v: serde_json::Value) -> Result<Schema> {
188        Ok(serde_json::from_value(v)?)
189    }
190}
191
192impl TryFrom<&str> for Schema {
193    type Error = crate::error::Error;
194    fn try_from(s: &str) -> Result<Schema> {
195        Ok(serde_json::from_str(s)?)
196    }
197}
198
199impl TryFrom<String> for Schema {
200    type Error = crate::error::Error;
201    fn try_from(s: String) -> Result<Schema> {
202        Ok(serde_json::from_str(&s)?)
203    }
204}
205
206impl TryFrom<&str> for SchemaDefinition {
207    type Error = crate::error::Error;
208    fn try_from(s: &str) -> Result<SchemaDefinition> {
209        Ok(serde_json::from_str(s)?)
210    }
211}
212
213impl TryFrom<String> for SchemaDefinition {
214    type Error = crate::error::Error;
215    fn try_from(s: String) -> Result<SchemaDefinition> {
216        Ok(serde_json::from_str(&s)?)
217    }
218}
219
220/// Represents a full JSON Schema Document, except when it is a boolean
221#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
222pub(crate) struct SchemaDefinition {
223    #[serde(rename = "$id")]
224    pub id: Option<SchemaId>,
225
226    #[serde(rename = "$schema")]
227    pub schema: Option<Url>,
228    pub description: Option<String>,
229    // pub properties: HashMap<String, Property>,
230    pub dependencies: Option<HashMap<String, Vec<String>>>,
231
232    #[serde(flatten)]
233    pub specification: Option<Property>,
234
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub definitions: Option<HashMap<String, SchemaDefinition>>,
237}