corvus_json_schema 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +390 -0
- data/Cargo.toml +8 -0
- data/LICENSE +201 -0
- data/README.md +111 -0
- data/VERSIONHISTORY.md +7 -0
- data/ext/corvus_json_schema/Cargo.toml +18 -0
- data/ext/corvus_json_schema/extconf.rb +6 -0
- data/ext/corvus_json_schema/rustfmt.toml +2 -0
- data/ext/corvus_json_schema/src/lib.rs +643 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/Cargo.toml +35 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/LICENSE +201 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/README.md +139 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/compiler.rs +1118 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/dialect.rs +108 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/document.rs +905 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval/plan/fused.rs +1187 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval/plan.rs +1728 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval.rs +1451 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/formats.rs +1021 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/instance.rs +228 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/lib.rs +189 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/loader.rs +403 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/metaschemas.rs +29 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/node.rs +417 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/numbers.rs +244 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/options.rs +108 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/pattern.rs +1591 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/results.rs +316 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/uri.rs +274 -0
- data/lib/corvus_json_schema/version.rb +5 -0
- data/lib/corvus_json_schema.rb +65 -0
- metadata +94 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
//! The instance values the evaluator reads: anything that can show itself as one of the six JSON kinds, with arrays and
|
|
2
|
+
//! objects read in place. `&serde_json::Value` is one; a host language's own values (for example Python objects, read
|
|
3
|
+
//! through its C API) can be another, so that evaluation needs no conversion to `serde_json::Value` first.
|
|
4
|
+
|
|
5
|
+
use serde_json::{Map, Number, Value};
|
|
6
|
+
|
|
7
|
+
/// A JSON value the evaluator can read in place. Cheap to copy (a reference or a handle); values reached through it
|
|
8
|
+
/// (items, property values, strings) live as long as `'a`.
|
|
9
|
+
pub trait Instance<'a>: Copy {
|
|
10
|
+
/// An array of instances.
|
|
11
|
+
type Array: ArrayView<'a, Item = Self>;
|
|
12
|
+
/// An object of named instances.
|
|
13
|
+
type Object: ObjectView<'a, Item = Self>;
|
|
14
|
+
|
|
15
|
+
/// The value's kind and content.
|
|
16
|
+
fn view(self) -> View<'a, Self>;
|
|
17
|
+
|
|
18
|
+
/// The value's kind alone (for type tests, which need no content).
|
|
19
|
+
#[inline]
|
|
20
|
+
fn kind(self) -> Kind {
|
|
21
|
+
match self.view() {
|
|
22
|
+
View::Null => Kind::Null,
|
|
23
|
+
View::Bool(_) => Kind::Bool,
|
|
24
|
+
View::Number(_) => Kind::Number,
|
|
25
|
+
View::String(_) => Kind::String,
|
|
26
|
+
View::Array(_) => Kind::Array,
|
|
27
|
+
View::Object(_) => Kind::Object,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// The kind of a JSON value. The discriminants are the evaluator's type bits, so a type test is one mask operation
|
|
33
|
+
/// (and a match that produces a kind compiles to a table, not a jump per value).
|
|
34
|
+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
35
|
+
#[repr(u8)]
|
|
36
|
+
pub enum Kind {
|
|
37
|
+
Null = 1,
|
|
38
|
+
Bool = 2,
|
|
39
|
+
Object = 4,
|
|
40
|
+
Array = 8,
|
|
41
|
+
Number = 16,
|
|
42
|
+
String = 32,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// What an instance is.
|
|
46
|
+
pub enum View<'a, I: Instance<'a>> {
|
|
47
|
+
Null,
|
|
48
|
+
Bool(bool),
|
|
49
|
+
Number(Number),
|
|
50
|
+
String(&'a str),
|
|
51
|
+
Array(I::Array),
|
|
52
|
+
Object(I::Object),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// The items of an array.
|
|
56
|
+
pub trait ArrayView<'a>: Copy {
|
|
57
|
+
type Item: Instance<'a>;
|
|
58
|
+
|
|
59
|
+
fn len(self) -> usize;
|
|
60
|
+
|
|
61
|
+
fn is_empty(self) -> bool {
|
|
62
|
+
self.len() == 0
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// The item at an index (which must be below `len`).
|
|
66
|
+
fn get(self, index: usize) -> Self::Item;
|
|
67
|
+
|
|
68
|
+
fn iter(self) -> impl Iterator<Item = Self::Item>;
|
|
69
|
+
|
|
70
|
+
/// The items from an index on (none when it is past the end).
|
|
71
|
+
fn tail(self, start: usize) -> impl Iterator<Item = Self::Item> {
|
|
72
|
+
self.iter().skip(start)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// The properties of an object, in their order.
|
|
77
|
+
pub trait ObjectView<'a>: Copy {
|
|
78
|
+
type Item: Instance<'a>;
|
|
79
|
+
|
|
80
|
+
fn len(self) -> usize;
|
|
81
|
+
|
|
82
|
+
fn is_empty(self) -> bool {
|
|
83
|
+
self.len() == 0
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// The value of a property.
|
|
87
|
+
fn get(self, name: &str) -> Option<Self::Item>;
|
|
88
|
+
|
|
89
|
+
fn contains_key(self, name: &str) -> bool {
|
|
90
|
+
self.get(name).is_some()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fn iter(self) -> impl Iterator<Item = (&'a str, Self::Item)>;
|
|
94
|
+
|
|
95
|
+
fn values(self) -> impl Iterator<Item = Self::Item> {
|
|
96
|
+
self.iter().map(|(_, v)| v)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
impl<'a> Instance<'a> for &'a Value {
|
|
101
|
+
type Array = &'a [Value];
|
|
102
|
+
type Object = &'a Map<String, Value>;
|
|
103
|
+
|
|
104
|
+
#[inline(always)]
|
|
105
|
+
fn view(self) -> View<'a, Self> {
|
|
106
|
+
match self {
|
|
107
|
+
Value::Null => View::Null,
|
|
108
|
+
Value::Bool(b) => View::Bool(*b),
|
|
109
|
+
Value::Number(n) => View::Number(n.clone()),
|
|
110
|
+
Value::String(s) => View::String(s),
|
|
111
|
+
Value::Array(a) => View::Array(a.as_slice()),
|
|
112
|
+
Value::Object(o) => View::Object(o),
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#[inline(always)]
|
|
117
|
+
fn kind(self) -> Kind {
|
|
118
|
+
match self {
|
|
119
|
+
Value::Null => Kind::Null,
|
|
120
|
+
Value::Bool(_) => Kind::Bool,
|
|
121
|
+
Value::Number(_) => Kind::Number,
|
|
122
|
+
Value::String(_) => Kind::String,
|
|
123
|
+
Value::Array(_) => Kind::Array,
|
|
124
|
+
Value::Object(_) => Kind::Object,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
impl<'a> ArrayView<'a> for &'a [Value] {
|
|
130
|
+
type Item = &'a Value;
|
|
131
|
+
|
|
132
|
+
#[inline(always)]
|
|
133
|
+
fn len(self) -> usize {
|
|
134
|
+
<[Value]>::len(self)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#[inline(always)]
|
|
138
|
+
fn get(self, index: usize) -> &'a Value {
|
|
139
|
+
&self[index]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#[inline(always)]
|
|
143
|
+
fn iter(self) -> impl Iterator<Item = &'a Value> {
|
|
144
|
+
<[Value]>::iter(self)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#[inline(always)]
|
|
148
|
+
fn tail(self, start: usize) -> impl Iterator<Item = &'a Value> {
|
|
149
|
+
self.get(start..).unwrap_or_default().iter()
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Objects up to this size are searched by scanning their keys: comparing lengths first, that is cheaper than hashing
|
|
154
|
+
/// the name with serde_json's SipHash.
|
|
155
|
+
const LINEAR_KEYS: usize = 32;
|
|
156
|
+
|
|
157
|
+
impl<'a> ObjectView<'a> for &'a Map<String, Value> {
|
|
158
|
+
type Item = &'a Value;
|
|
159
|
+
|
|
160
|
+
#[inline(always)]
|
|
161
|
+
fn len(self) -> usize {
|
|
162
|
+
Map::len(self)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#[inline]
|
|
166
|
+
fn get(self, name: &str) -> Option<&'a Value> {
|
|
167
|
+
if Map::len(self) <= LINEAR_KEYS {
|
|
168
|
+
Map::iter(self).find(|(k, _)| str_eq(k, name)).map(|(_, v)| v)
|
|
169
|
+
} else {
|
|
170
|
+
Map::get(self, name)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[inline]
|
|
175
|
+
fn contains_key(self, name: &str) -> bool {
|
|
176
|
+
if Map::len(self) <= LINEAR_KEYS { self.keys().any(|k| str_eq(k, name)) } else { Map::contains_key(self, name) }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
#[inline(always)]
|
|
180
|
+
fn iter(self) -> impl Iterator<Item = (&'a str, &'a Value)> {
|
|
181
|
+
Map::iter(self).map(|(k, v)| (k.as_str(), v))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#[inline(always)]
|
|
185
|
+
fn values(self) -> impl Iterator<Item = &'a Value> {
|
|
186
|
+
Map::values(self)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// String equality with the length test and short comparisons inline (property names are short).
|
|
191
|
+
#[inline(always)]
|
|
192
|
+
pub(crate) fn str_eq(a: &str, b: &str) -> bool {
|
|
193
|
+
let (a, b) = (a.as_bytes(), b.as_bytes());
|
|
194
|
+
if a.len() != b.len() {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
let n = a.len();
|
|
198
|
+
if n <= 8 {
|
|
199
|
+
if n >= 4 {
|
|
200
|
+
// Two overlapping four-byte words cover every byte (one load each, no per-byte bounds checks).
|
|
201
|
+
let word = |s: &[u8], i: usize| u32::from_le_bytes(s[i..i + 4].try_into().unwrap());
|
|
202
|
+
return word(a, 0) == word(b, 0) && word(a, n - 4) == word(b, n - 4);
|
|
203
|
+
}
|
|
204
|
+
return a == b;
|
|
205
|
+
}
|
|
206
|
+
// Eight-byte words from the start, then one ending at the last byte (overlapping the previous one): property names
|
|
207
|
+
// are short enough that a call to memcmp costs more than the compare.
|
|
208
|
+
let word = |s: &[u8], i: usize| u64::from_le_bytes(s[i..i + 8].try_into().unwrap());
|
|
209
|
+
let mut i = 0;
|
|
210
|
+
while i + 8 < n {
|
|
211
|
+
if word(a, i) != word(b, i) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
i += 8;
|
|
215
|
+
}
|
|
216
|
+
word(a, n - 8) == word(b, n - 8)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// The kinds are the evaluator's type bits.
|
|
220
|
+
const _: () = {
|
|
221
|
+
use crate::node::type_mask;
|
|
222
|
+
assert!(Kind::Null as u8 == type_mask::NULL);
|
|
223
|
+
assert!(Kind::Bool as u8 == type_mask::BOOLEAN);
|
|
224
|
+
assert!(Kind::Object as u8 == type_mask::OBJECT);
|
|
225
|
+
assert!(Kind::Array as u8 == type_mask::ARRAY);
|
|
226
|
+
assert!(Kind::Number as u8 == type_mask::NUMBER);
|
|
227
|
+
assert!(Kind::String as u8 == type_mask::STRING);
|
|
228
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
//! A high-performance JSON Schema evaluator (draft 4, 6, 7, 2019-09 and 2020-12), ported from the Corvus.Text.Json V5
|
|
2
|
+
//! runtime evaluator (`Corvus.Text.Json.RuntimeEvaluator`).
|
|
3
|
+
//!
|
|
4
|
+
//! A schema is compiled once into a node graph with pre-digested keyword data, then any number of
|
|
5
|
+
//! `serde_json::Value` instances are evaluated against it: fast (fail-fast, no reporting) with
|
|
6
|
+
//! [`Validator::is_valid`], or exhaustively with a [`JsonSchemaResultsCollector`] for Basic, Detailed or Verbose
|
|
7
|
+
//! results and annotations, with the same rows (paths, messages, order) as the C# collector.
|
|
8
|
+
//!
|
|
9
|
+
//! ```
|
|
10
|
+
//! use serde_json::json;
|
|
11
|
+
//!
|
|
12
|
+
//! let validator = corvus_json_schema::compile(&json!({
|
|
13
|
+
//! "type": "object",
|
|
14
|
+
//! "properties": { "id": { "type": "integer", "minimum": 1 } },
|
|
15
|
+
//! "required": ["id"]
|
|
16
|
+
//! }))
|
|
17
|
+
//! .unwrap();
|
|
18
|
+
//! assert!(validator.is_valid(&json!({ "id": 3 })));
|
|
19
|
+
//! assert!(!validator.is_valid(&json!({ "id": 0 })));
|
|
20
|
+
//! ```
|
|
21
|
+
|
|
22
|
+
mod compiler;
|
|
23
|
+
mod dialect;
|
|
24
|
+
mod document;
|
|
25
|
+
mod eval;
|
|
26
|
+
mod formats;
|
|
27
|
+
mod instance;
|
|
28
|
+
mod loader;
|
|
29
|
+
#[rustfmt::skip]
|
|
30
|
+
mod metaschemas;
|
|
31
|
+
mod node;
|
|
32
|
+
mod numbers;
|
|
33
|
+
mod options;
|
|
34
|
+
mod pattern;
|
|
35
|
+
mod results;
|
|
36
|
+
mod uri;
|
|
37
|
+
|
|
38
|
+
use std::sync::Arc;
|
|
39
|
+
|
|
40
|
+
use serde_json::Value;
|
|
41
|
+
|
|
42
|
+
pub use dialect::Dialect;
|
|
43
|
+
pub use document::{JsonDocument, JsonDocumentArray, JsonDocumentObject, JsonDocumentValue, JsonParseError};
|
|
44
|
+
pub use instance::{ArrayView, Instance, Kind, ObjectView, View};
|
|
45
|
+
pub use options::{
|
|
46
|
+
CompileOptions, DocumentResolver, FormatValidator, SchemaCompilationError, SchemaEvaluationDepthError,
|
|
47
|
+
};
|
|
48
|
+
pub use results::{
|
|
49
|
+
Annotation, JsonSchemaResultsCollector, ResultsLevel, SchemaResult, collect_annotations, encode_pointer_segment,
|
|
50
|
+
enumerate_annotations, schema_location_fragment,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/// The version of this crate.
|
|
54
|
+
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
55
|
+
|
|
56
|
+
/// Why JSON text could not be validated.
|
|
57
|
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
58
|
+
pub enum JsonValidationError {
|
|
59
|
+
/// The text is not valid JSON.
|
|
60
|
+
InvalidJson(JsonParseError),
|
|
61
|
+
/// Evaluation recursed in place beyond the maximum depth.
|
|
62
|
+
DepthExceeded(SchemaEvaluationDepthError),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
impl std::fmt::Display for JsonValidationError {
|
|
66
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
67
|
+
match self {
|
|
68
|
+
JsonValidationError::InvalidJson(e) => write!(f, "invalid JSON: {e}"),
|
|
69
|
+
JsonValidationError::DepthExceeded(e) => e.fmt(f),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
impl std::error::Error for JsonValidationError {
|
|
75
|
+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
76
|
+
match self {
|
|
77
|
+
JsonValidationError::InvalidJson(e) => Some(e),
|
|
78
|
+
JsonValidationError::DepthExceeded(e) => Some(e),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// A compiled schema. Cheap to clone and safe to share between threads.
|
|
84
|
+
#[derive(Clone)]
|
|
85
|
+
pub struct Validator {
|
|
86
|
+
program: Arc<eval::Program>,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
impl std::fmt::Debug for Validator {
|
|
90
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
91
|
+
f.debug_struct("Validator").field("nodes", &self.program.nodes.len()).finish()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Compiles a schema with the default options (2020-12 for documents without `$schema`, `format` as an annotation
|
|
96
|
+
/// unless the vocabularies assert it).
|
|
97
|
+
pub fn compile(schema: &Value) -> Result<Validator, SchemaCompilationError> {
|
|
98
|
+
compile_with(schema, &CompileOptions::default())
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Compiles a schema.
|
|
102
|
+
pub fn compile_with(schema: &Value, options: &CompileOptions) -> Result<Validator, SchemaCompilationError> {
|
|
103
|
+
let compiled = compiler::SchemaCompiler::compile(schema.clone(), options)?;
|
|
104
|
+
Ok(Validator::from_compiled(compiled, options))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// Compiles the schema document at `uri`, fetched through `options.resolve_document` (or one of the standard
|
|
108
|
+
/// metaschemas).
|
|
109
|
+
pub fn compile_from_uri(uri: &str, options: &CompileOptions) -> Result<Validator, SchemaCompilationError> {
|
|
110
|
+
let compiled = compiler::SchemaCompiler::compile_from_uri(uri, options)?;
|
|
111
|
+
Ok(Validator::from_compiled(compiled, options))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
impl Validator {
|
|
115
|
+
fn from_compiled(c: compiler::CompiledSchema, options: &CompileOptions) -> Validator {
|
|
116
|
+
let program = eval::Program::new(
|
|
117
|
+
c.nodes,
|
|
118
|
+
c.root,
|
|
119
|
+
c.uses_dynamic_scope,
|
|
120
|
+
options.max_depth,
|
|
121
|
+
options.formats.clone(),
|
|
122
|
+
c.documents,
|
|
123
|
+
c.annotation_sources,
|
|
124
|
+
options.assert_format.is_some(),
|
|
125
|
+
);
|
|
126
|
+
Validator { program: Arc::new(program) }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// Whether the instance is valid. A schema that recursed in place beyond the maximum depth is reported as
|
|
130
|
+
/// invalid; use [`Validator::validate`] to tell the two apart.
|
|
131
|
+
#[inline]
|
|
132
|
+
pub fn is_valid(&self, instance: &Value) -> bool {
|
|
133
|
+
eval::Evaluator::new(&self.program, None).validate(instance)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Whether the instance is valid, or an error if evaluation recursed in place beyond the maximum depth.
|
|
137
|
+
pub fn validate(&self, instance: &Value) -> Result<bool, SchemaEvaluationDepthError> {
|
|
138
|
+
let mut e = eval::Evaluator::new(&self.program, None);
|
|
139
|
+
let ok = e.validate(instance);
|
|
140
|
+
if e.depth_exceeded { Err(SchemaEvaluationDepthError) } else { Ok(ok) }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Evaluates the instance exhaustively, reporting to the collector. Returns whether it is valid.
|
|
144
|
+
pub fn evaluate(
|
|
145
|
+
&self,
|
|
146
|
+
instance: &Value,
|
|
147
|
+
collector: &mut JsonSchemaResultsCollector,
|
|
148
|
+
) -> Result<bool, SchemaEvaluationDepthError> {
|
|
149
|
+
self.evaluate_instance(instance, collector)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// [`Validator::validate`] for any [`Instance`]: a value read in place, such as a host language's own objects,
|
|
153
|
+
/// with no conversion to `serde_json::Value`.
|
|
154
|
+
pub fn validate_instance<'a, I: Instance<'a>>(&self, instance: I) -> Result<bool, SchemaEvaluationDepthError> {
|
|
155
|
+
let mut e = eval::Evaluator::new(&self.program, None);
|
|
156
|
+
let ok = e.validate(instance);
|
|
157
|
+
if e.depth_exceeded { Err(SchemaEvaluationDepthError) } else { Ok(ok) }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// Whether the JSON text is valid. The text is parsed into the thread's reused buffers and evaluated in place: in
|
|
161
|
+
/// the steady state nothing is allocated. Use [`JsonDocument`] to validate the same text more than once.
|
|
162
|
+
pub fn validate_json(&self, json: &str) -> Result<bool, JsonValidationError> {
|
|
163
|
+
JsonDocument::with_document(json, |d| self.validate_instance(d.root()))
|
|
164
|
+
.map_err(JsonValidationError::InvalidJson)?
|
|
165
|
+
.map_err(JsonValidationError::DepthExceeded)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// [`Validator::evaluate`] for JSON text, parsed as [`Validator::validate_json`] parses it.
|
|
169
|
+
pub fn evaluate_json(
|
|
170
|
+
&self,
|
|
171
|
+
json: &str,
|
|
172
|
+
collector: &mut JsonSchemaResultsCollector,
|
|
173
|
+
) -> Result<bool, JsonValidationError> {
|
|
174
|
+
JsonDocument::with_document(json, |d| self.evaluate_instance(d.root(), collector))
|
|
175
|
+
.map_err(JsonValidationError::InvalidJson)?
|
|
176
|
+
.map_err(JsonValidationError::DepthExceeded)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// [`Validator::evaluate`] for any [`Instance`].
|
|
180
|
+
pub fn evaluate_instance<'a, I: Instance<'a>>(
|
|
181
|
+
&self,
|
|
182
|
+
instance: I,
|
|
183
|
+
collector: &mut JsonSchemaResultsCollector,
|
|
184
|
+
) -> Result<bool, SchemaEvaluationDepthError> {
|
|
185
|
+
let mut e = eval::Evaluator::new(&self.program, Some(collector));
|
|
186
|
+
let ok = e.evaluate(instance);
|
|
187
|
+
if e.depth_exceeded { Err(SchemaEvaluationDepthError) } else { Ok(ok) }
|
|
188
|
+
}
|
|
189
|
+
}
|