chore: Convert `ValidationError.instance_path` to a separate struct
This commit is contained in:
parent
bdbe656a99
commit
674da91b63
|
@ -7,6 +7,7 @@
|
|||
- The `propertyNames` validator now contains the parent object in its `instance` attribute instead of individual properties as strings.
|
||||
- Improved error message for the `additionalProperties` validator. After - `Additional properties are not allowed ('faz' was unexpected)`, before - `False schema does not allow '"faz"'`.
|
||||
- The `additionalProperties` validator emits a single error for all unexpected properties instead of separate errors for each unexpected property.
|
||||
- `ValidationError.instance_path` is now a separate struct, that can be transformed to `Vec<String>` or JSON Pointer of type `String`.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ fn main() -> Result<(), CompilationError> {
|
|||
if let Err(errors) = result {
|
||||
for error in errors {
|
||||
println!("Validation error: {}", error);
|
||||
println!("Instance path: {:?}", error.instance_path);
|
||||
println!("Instance path: {}", error.instance_path);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -41,6 +41,7 @@ fn main() -> Result<(), CompilationError> {
|
|||
```
|
||||
|
||||
Each error has an `instance_path` attribute that indicates the path to the erroneous part within the validated instance.
|
||||
It could be transformed to JSON Pointer via `.to_string()` or to `Vec<String>` via `.into_vec()`.
|
||||
|
||||
If you only need to know whether document is valid or not (which is faster):
|
||||
|
||||
|
@ -95,7 +96,7 @@ Ratios are given against compiled `JSONSchema` using its `validate`. The `is_val
|
|||
| ------------- | ----------------------- | ----------------------- | --------------------- | ---------------------- |
|
||||
| Big valid | - | 95.008 ms (**x12.27**) | 7.74 ms | 5.785 ms (**x0.74**) |
|
||||
| Small valid | 2.04 us (**x4.18**) | 3.67 us (**x7.53**) | 487.38 ns | 113.3 ns (**x0.23**) |
|
||||
| Small invalid | 397.52 ns (**x0.60**) | 3.73 us (**x5.67**) | 657.49 ns | 5.53 ns (**x0.008**) |
|
||||
| Small invalid | 397.52 ns (**x0.64**) | 3.73 us (**x6.02**) | 619.32 ns | 5.53 ns (**x0.008**) |
|
||||
|
||||
Unfortunately, `jsonschema_valid` mistakenly considers the Kubernetes Open API schema as invalid and therefore can't be compared with other libraries in this case.
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ num-cmp = ">= 0.1"
|
|||
idna = ">= 0.2"
|
||||
ahash = "0.7"
|
||||
structopt = { version = ">= 0.3", optional = true }
|
||||
itoa = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = ">= 0.1"
|
||||
|
|
|
@ -7,7 +7,8 @@ pub(crate) mod options;
|
|||
use crate::{
|
||||
error::{CompilationError, ErrorIterator},
|
||||
keywords,
|
||||
keywords::{InstancePath, Validators},
|
||||
keywords::Validators,
|
||||
paths::InstancePath,
|
||||
resolver::Resolver,
|
||||
};
|
||||
use context::CompilationContext;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
//! Error types
|
||||
use crate::primitive_type::{PrimitiveType, PrimitiveTypesBitMap};
|
||||
use crate::{
|
||||
paths::JSONPointer,
|
||||
primitive_type::{PrimitiveType, PrimitiveTypesBitMap},
|
||||
};
|
||||
use serde_json::{Map, Number, Value};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
@ -51,7 +54,7 @@ pub struct ValidationError<'a> {
|
|||
/// Type of validation error
|
||||
pub kind: ValidationErrorKind,
|
||||
/// Path of the property that failed validation
|
||||
pub instance_path: Vec<String>,
|
||||
pub instance_path: JSONPointer,
|
||||
}
|
||||
|
||||
/// An iterator over instances of `ValidationError` that represent validation error for the
|
||||
|
@ -188,7 +191,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn additional_items(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: usize,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -199,7 +202,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn additional_properties(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
unexpected: Vec<String>,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -209,7 +212,7 @@ impl<'a> ValidationError<'a> {
|
|||
kind: ValidationErrorKind::AdditionalProperties { unexpected },
|
||||
}
|
||||
}
|
||||
pub(crate) fn any_of(instance_path: Vec<String>, instance: &'a Value) -> ValidationError<'a> {
|
||||
pub(crate) fn any_of(instance_path: JSONPointer, instance: &'a Value) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path,
|
||||
instance: Cow::Borrowed(instance),
|
||||
|
@ -217,7 +220,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn constant_array(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
expected_value: &[Value],
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -230,7 +233,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn constant_boolean(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
expected_value: bool,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -243,7 +246,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn constant_null(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
|
@ -255,7 +258,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn constant_number(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
expected_value: &Number,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -268,7 +271,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn constant_object(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
expected_value: &Map<String, Value>,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -281,7 +284,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn constant_string(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
expected_value: &str,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -293,7 +296,7 @@ impl<'a> ValidationError<'a> {
|
|||
},
|
||||
}
|
||||
}
|
||||
pub(crate) fn contains(instance_path: Vec<String>, instance: &'a Value) -> ValidationError<'a> {
|
||||
pub(crate) fn contains(instance_path: JSONPointer, instance: &'a Value) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path,
|
||||
instance: Cow::Borrowed(instance),
|
||||
|
@ -301,7 +304,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn content_encoding(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
encoding: &str,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -314,7 +317,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn content_media_type(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
media_type: &str,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -327,7 +330,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn enumeration(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
options: &Value,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -340,7 +343,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn exclusive_maximum(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: f64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -351,7 +354,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn exclusive_minimum(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: f64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -362,7 +365,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn false_schema(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
|
@ -373,13 +376,13 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
pub(crate) fn file_not_found(error: io::Error) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::FileNotFound { error },
|
||||
}
|
||||
}
|
||||
pub(crate) fn format(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
format: &'static str,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -391,34 +394,34 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
pub(crate) fn from_utf8(error: FromUtf8Error) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::FromUtf8 { error },
|
||||
}
|
||||
}
|
||||
pub(crate) fn json_parse(error: serde_json::Error) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::JSONParse { error },
|
||||
}
|
||||
}
|
||||
pub(crate) fn invalid_reference(reference: String) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::InvalidReference { reference },
|
||||
}
|
||||
}
|
||||
pub(crate) fn invalid_url(error: url::ParseError) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::InvalidURL { error },
|
||||
}
|
||||
}
|
||||
pub(crate) fn max_items(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: u64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -429,7 +432,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn maximum(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: f64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -440,7 +443,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn max_length(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: u64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -451,7 +454,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn max_properties(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: u64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -462,7 +465,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn min_items(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: u64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -473,7 +476,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn minimum(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: f64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -484,7 +487,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn min_length(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: u64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -495,7 +498,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn min_properties(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
limit: u64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -506,7 +509,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn multiple_of(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
multiple_of: f64,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -517,7 +520,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn not(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
schema: Value,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -528,7 +531,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn one_of_multiple_valid(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
|
@ -538,7 +541,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn one_of_not_valid(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
|
@ -548,7 +551,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn pattern(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
pattern: String,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -559,7 +562,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn property_names(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
error: ValidationError<'a>,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -572,7 +575,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn required(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
property: String,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -585,20 +588,20 @@ impl<'a> ValidationError<'a> {
|
|||
#[cfg(any(feature = "reqwest", test))]
|
||||
pub(crate) fn reqwest(error: reqwest::Error) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::Reqwest { error },
|
||||
}
|
||||
}
|
||||
pub(crate) fn schema() -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::Schema,
|
||||
}
|
||||
}
|
||||
pub(crate) fn single_type_error(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
type_name: PrimitiveType,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -611,7 +614,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn multiple_type_error(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
types: PrimitiveTypesBitMap,
|
||||
) -> ValidationError<'a> {
|
||||
|
@ -624,7 +627,7 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
}
|
||||
pub(crate) fn unique_items(
|
||||
instance_path: Vec<String>,
|
||||
instance_path: JSONPointer,
|
||||
instance: &'a Value,
|
||||
) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
|
@ -635,14 +638,14 @@ impl<'a> ValidationError<'a> {
|
|||
}
|
||||
pub(crate) fn unknown_reference_scheme(scheme: String) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::UnknownReferenceScheme { scheme },
|
||||
}
|
||||
}
|
||||
pub(crate) fn utf8(error: Utf8Error) -> ValidationError<'a> {
|
||||
ValidationError {
|
||||
instance_path: Vec::new(),
|
||||
instance_path: JSONPointer::default(),
|
||||
instance: Cow::Owned(Value::Null),
|
||||
kind: ValidationErrorKind::Utf8 { error },
|
||||
}
|
||||
|
@ -902,14 +905,18 @@ impl fmt::Display for ValidationError<'_> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::JSONSchema;
|
||||
use crate::{paths::PathChunk, JSONSchema};
|
||||
use serde_json::json;
|
||||
use test_case::test_case;
|
||||
|
||||
#[test]
|
||||
fn single_type_error() {
|
||||
let instance = json!(42);
|
||||
let err = ValidationError::single_type_error(vec![], &instance, PrimitiveType::String);
|
||||
let err = ValidationError::single_type_error(
|
||||
JSONPointer::default(),
|
||||
&instance,
|
||||
PrimitiveType::String,
|
||||
);
|
||||
assert_eq!(err.to_string(), "'42' is not of type 'string'")
|
||||
}
|
||||
|
||||
|
@ -917,7 +924,7 @@ mod tests {
|
|||
fn multiple_types_error() {
|
||||
let instance = json!(42);
|
||||
let err = ValidationError::multiple_type_error(
|
||||
vec![],
|
||||
JSONPointer::default(),
|
||||
&instance,
|
||||
vec![PrimitiveType::String, PrimitiveType::Number].into(),
|
||||
);
|
||||
|
@ -950,17 +957,17 @@ mod tests {
|
|||
let error = result.next().expect("validation error");
|
||||
|
||||
assert!(result.next().is_none());
|
||||
assert_eq!(error.instance_path, expected);
|
||||
assert_eq!(error.instance_path, JSONPointer::from(expected));
|
||||
}
|
||||
|
||||
#[test_case(true, &json!([1, {"foo": ["42"]}]), &["0"])]
|
||||
#[test_case(true, &json!(["a", {"foo": [42]}]), &["1", "foo", "0"])]
|
||||
#[test_case(false, &json!([1, {"foo": ["42"]}]), &["0"])]
|
||||
#[test_case(false, &json!(["a", {"foo": [42]}]), &["1", "foo", "0"])]
|
||||
#[test_case(true, &json!([1, {"foo": ["42"]}]), &[PathChunk::Index(0)])]
|
||||
#[test_case(true, &json!(["a", {"foo": [42]}]), &[PathChunk::Index(1), PathChunk::Name("foo".to_string()), PathChunk::Index(0)])]
|
||||
#[test_case(false, &json!([1, {"foo": ["42"]}]), &[PathChunk::Index(0)])]
|
||||
#[test_case(false, &json!(["a", {"foo": [42]}]), &[PathChunk::Index(1), PathChunk::Name("foo".to_string()), PathChunk::Index(0)])]
|
||||
fn instance_path_properties_and_arrays(
|
||||
additional_items: bool,
|
||||
instance: &Value,
|
||||
expected: &[&str],
|
||||
expected: &[PathChunk],
|
||||
) {
|
||||
let schema = json!(
|
||||
{
|
||||
|
@ -991,14 +998,18 @@ mod tests {
|
|||
let error = result.next().expect("validation error");
|
||||
|
||||
assert!(result.next().is_none());
|
||||
assert_eq!(error.instance_path, expected);
|
||||
assert_eq!(error.instance_path, JSONPointer::from(expected));
|
||||
}
|
||||
|
||||
#[test_case(true, &json!([[1, 2, 3], [4, "5", 6], [7, 8, 9]]), &["1", "1"])]
|
||||
#[test_case(false, &json!([[1, 2, 3], [4, "5", 6], [7, 8, 9]]), &["1", "1"])]
|
||||
#[test_case(true, &json!([[1, 2, 3], [4, 5, 6], 42]), &["2"])]
|
||||
#[test_case(false, &json!([[1, 2, 3], [4, 5, 6], 42]), &["2"])]
|
||||
fn instance_path_nested_arrays(additional_items: bool, instance: &Value, expected: &[&str]) {
|
||||
#[test_case(true, &json!([[1, 2, 3], [4, "5", 6], [7, 8, 9]]), &[PathChunk::Index(1), PathChunk::Index(1)])]
|
||||
#[test_case(false, &json!([[1, 2, 3], [4, "5", 6], [7, 8, 9]]), &[PathChunk::Index(1), PathChunk::Index(1)])]
|
||||
#[test_case(true, &json!([[1, 2, 3], [4, 5, 6], 42]), &[PathChunk::Index(2)])]
|
||||
#[test_case(false, &json!([[1, 2, 3], [4, 5, 6], 42]), &[PathChunk::Index(2)])]
|
||||
fn instance_path_nested_arrays(
|
||||
additional_items: bool,
|
||||
instance: &Value,
|
||||
expected: &[PathChunk],
|
||||
) {
|
||||
let schema = json!(
|
||||
{
|
||||
"additionalItems": additional_items,
|
||||
|
@ -1016,14 +1027,14 @@ mod tests {
|
|||
let error = result.next().expect("validation error");
|
||||
|
||||
assert!(result.next().is_none());
|
||||
assert_eq!(error.instance_path, expected);
|
||||
assert_eq!(error.instance_path, JSONPointer::from(expected));
|
||||
}
|
||||
|
||||
#[test_case(true, &json!([1, "a"]), &["1"])]
|
||||
#[test_case(false, &json!([1, "a"]), &["1"])]
|
||||
#[test_case(true, &json!([1, "a"]), &[PathChunk::Index(1)])]
|
||||
#[test_case(false, &json!([1, "a"]), &[PathChunk::Index(1)])]
|
||||
#[test_case(true, &json!(123), &[])]
|
||||
#[test_case(false, &json!(123), &[])]
|
||||
fn instance_path_arrays(additional_items: bool, instance: &Value, expected: &[&str]) {
|
||||
fn instance_path_arrays(additional_items: bool, instance: &Value, expected: &[PathChunk]) {
|
||||
let schema = json!(
|
||||
{
|
||||
"additionalItems": additional_items,
|
||||
|
@ -1038,6 +1049,6 @@ mod tests {
|
|||
let error = result.next().expect("validation error");
|
||||
|
||||
assert!(result.next().is_none());
|
||||
assert_eq!(error.instance_path, expected);
|
||||
assert_eq!(error.instance_path, JSONPointer::from(expected));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@ use crate::{
|
|||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{
|
||||
boolean::{FalseValidator, TrueValidator},
|
||||
format_validators, CompilationResult, InstancePath, Validators,
|
||||
format_validators, CompilationResult, Validators,
|
||||
},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{format_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use ahash::AHashMap;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{CompilationError, ErrorIterator},
|
||||
keywords::{format_vec_of_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_vec_of_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{format_vec_of_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_vec_of_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::keywords::InstancePath;
|
||||
use crate::paths::InstancePath;
|
||||
|
||||
use crate::{
|
||||
compilation::JSONSchema,
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
use serde_json::{Map, Number, Value};
|
||||
use std::f64::EPSILON;
|
||||
|
||||
use super::InstancePath;
|
||||
use crate::paths::InstancePath;
|
||||
|
||||
struct ConstArrayValidator {
|
||||
value: Vec<Value>,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, ErrorIterator, ValidationError},
|
||||
keywords::{format_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -4,7 +4,8 @@ use crate::{
|
|||
content_encoding::{ContentEncodingCheckType, ContentEncodingConverterType},
|
||||
content_media_type::ContentMediaTypeCheckType,
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -2,9 +2,9 @@ use crate::{
|
|||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{no_error, CompilationError, ErrorIterator},
|
||||
keywords::{
|
||||
format_key_value_validators, required::RequiredValidator, CompilationResult, InstancePath,
|
||||
Validators,
|
||||
format_key_value_validators, required::RequiredValidator, CompilationResult, Validators,
|
||||
},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{helpers, CompilationResult, InstancePath},
|
||||
keywords::{helpers, CompilationResult},
|
||||
paths::InstancePath,
|
||||
primitive_type::{PrimitiveType, PrimitiveTypesBitMap},
|
||||
validator::Validate,
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use num_cmp::NumCmp;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use num_cmp::NumCmp;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
Draft,
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{no_error, ErrorIterator},
|
||||
keywords::{format_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -3,8 +3,9 @@ use crate::{
|
|||
error::{no_error, ErrorIterator},
|
||||
keywords::{
|
||||
boolean::TrueValidator, format_validators, format_vec_of_validators, CompilationResult,
|
||||
InstancePath, Validators,
|
||||
Validators,
|
||||
},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{type_, CompilationResult, InstancePath},
|
||||
keywords::{type_, CompilationResult},
|
||||
paths::InstancePath,
|
||||
primitive_type::{PrimitiveType, PrimitiveTypesBitMap},
|
||||
validator::Validate,
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use num_cmp::NumCmp;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use num_cmp::NumCmp;
|
||||
|
|
|
@ -35,71 +35,10 @@ pub(crate) mod required;
|
|||
pub(crate) mod type_;
|
||||
pub(crate) mod unique_items;
|
||||
use crate::{error, validator::Validate};
|
||||
use std::{cell::RefCell, ops::Deref};
|
||||
|
||||
pub(crate) type CompilationResult = Result<BoxedValidator, error::CompilationError>;
|
||||
pub(crate) type BoxedValidator = Box<dyn Validate + Send + Sync>;
|
||||
pub(crate) type Validators = Vec<BoxedValidator>;
|
||||
pub(crate) type InstancePathInner = RefCell<Vec<PathChunk>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum PathChunk {
|
||||
Name(String),
|
||||
Index(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct InstancePath(InstancePathInner);
|
||||
|
||||
impl InstancePath {
|
||||
pub(crate) fn new(inner: InstancePathInner) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn push(&self, value: impl Into<PathChunk>) {
|
||||
self.borrow_mut().push(value.into())
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn pop(&self) {
|
||||
self.borrow_mut().pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for PathChunk {
|
||||
#[inline]
|
||||
fn from(value: String) -> Self {
|
||||
PathChunk::Name(value)
|
||||
}
|
||||
}
|
||||
impl From<usize> for PathChunk {
|
||||
#[inline]
|
||||
fn from(value: usize) -> Self {
|
||||
PathChunk::Index(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for InstancePath {
|
||||
type Target = InstancePathInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&InstancePath> for Vec<String> {
|
||||
#[inline]
|
||||
fn from(path: &InstancePath) -> Self {
|
||||
path.0
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|item| match item {
|
||||
PathChunk::Name(value) => value.to_string(),
|
||||
PathChunk::Index(idx) => idx.to_string(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn format_validators(validators: &[BoxedValidator]) -> String {
|
||||
match validators.len() {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, ErrorIterator, ValidationError},
|
||||
keywords::{format_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{format_vec_of_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_vec_of_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use regex::{Captures, Regex};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{no_error, CompilationError, ErrorIterator},
|
||||
keywords::{format_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use regex::Regex;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{no_error, CompilationError, ErrorIterator},
|
||||
keywords::{format_key_value_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_key_value_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, ErrorIterator, ValidationError},
|
||||
keywords::{format_validators, CompilationResult, InstancePath, Validators},
|
||||
keywords::{format_validators, CompilationResult, Validators},
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -9,7 +9,7 @@ use serde_json::Value;
|
|||
use std::borrow::Cow;
|
||||
use url::Url;
|
||||
|
||||
use super::InstancePath;
|
||||
use crate::paths::InstancePath;
|
||||
|
||||
pub(crate) struct RefValidator {
|
||||
reference: Url,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{no_error, CompilationError, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
paths::InstancePath,
|
||||
validator::Validate,
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
use serde_json::{Map, Number, Value};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use super::InstancePath;
|
||||
use crate::paths::InstancePath;
|
||||
|
||||
pub(crate) struct MultipleTypesValidator {
|
||||
types: PrimitiveTypesBitMap,
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use crate::{
|
||||
compilation::{context::CompilationContext, JSONSchema},
|
||||
error::{error, no_error, ErrorIterator, ValidationError},
|
||||
keywords::{CompilationResult, InstancePath},
|
||||
keywords::CompilationResult,
|
||||
validator::Validate,
|
||||
};
|
||||
use ahash::{AHashSet, AHasher};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::paths::InstancePath;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
// Based on implementation proposed by Sven Marnach:
|
||||
|
|
|
@ -47,12 +47,14 @@
|
|||
//! if let Err(errors) = result {
|
||||
//! for error in errors {
|
||||
//! println!("Validation error: {}", error);
|
||||
//! println!("Instance path: {:?}", error.instance_path);
|
||||
//! println!("Instance path: {}", error.instance_path);
|
||||
//! }
|
||||
//! }
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//! Each error has an `instance_path` attribute that indicates the path to the erroneous part within the validated instance.
|
||||
//! It could be transformed to JSON Pointer via `.to_string()` or to `Vec<String>` via `.into_vec()`.
|
||||
#![warn(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::doc_markdown,
|
||||
|
@ -81,10 +83,12 @@ mod content_encoding;
|
|||
mod content_media_type;
|
||||
pub mod error;
|
||||
mod keywords;
|
||||
pub mod paths;
|
||||
pub mod primitive_type;
|
||||
mod resolver;
|
||||
mod schemas;
|
||||
mod validator;
|
||||
|
||||
pub use compilation::{options::CompilationOptions, JSONSchema};
|
||||
pub use error::{CompilationError, ErrorIterator, ValidationError};
|
||||
pub use schemas::Draft;
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
//! Facilities for working with paths within schemas or validated instances.
|
||||
use std::fmt::Write;
|
||||
use std::{cell::RefCell, fmt, ops::Deref};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
/// JSON Pointer as a wrapper around individual path components
|
||||
pub struct JSONPointer(Vec<PathChunk>);
|
||||
|
||||
impl JSONPointer {
|
||||
/// JSON pointer as a vector of strings. Each component is casted to `String`.
|
||||
pub fn into_vec(self) -> Vec<String> {
|
||||
self.0
|
||||
.iter()
|
||||
.map(|item| match item {
|
||||
PathChunk::Name(value) => value.to_string(),
|
||||
PathChunk::Index(idx) => idx.to_string(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for JSONPointer {
|
||||
fn default() -> Self {
|
||||
JSONPointer(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for JSONPointer {
|
||||
fn fmt(&self, mut f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.0.is_empty() {
|
||||
for chunk in &self.0 {
|
||||
f.write_char('/')?;
|
||||
match chunk {
|
||||
PathChunk::Name(value) => f.write_str(value)?,
|
||||
PathChunk::Index(idx) => itoa::fmt(&mut f, *idx)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum PathChunk {
|
||||
Name(String),
|
||||
Index(usize),
|
||||
}
|
||||
|
||||
pub(crate) type InstancePathInner = RefCell<Vec<PathChunk>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct InstancePath(InstancePathInner);
|
||||
|
||||
impl InstancePath {
|
||||
pub(crate) fn new(inner: InstancePathInner) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn push(&self, value: impl Into<PathChunk>) {
|
||||
self.borrow_mut().push(value.into())
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn pop(&self) {
|
||||
self.borrow_mut().pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for PathChunk {
|
||||
#[inline]
|
||||
fn from(value: String) -> Self {
|
||||
PathChunk::Name(value)
|
||||
}
|
||||
}
|
||||
impl From<usize> for PathChunk {
|
||||
#[inline]
|
||||
fn from(value: usize) -> Self {
|
||||
PathChunk::Index(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for InstancePath {
|
||||
type Target = InstancePathInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&InstancePath> for JSONPointer {
|
||||
#[inline]
|
||||
fn from(path: &InstancePath) -> Self {
|
||||
JSONPointer(path.0.borrow().iter().map(|item| item.to_owned()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[&str]> for JSONPointer {
|
||||
#[inline]
|
||||
fn from(path: &[&str]) -> Self {
|
||||
JSONPointer(
|
||||
path.iter()
|
||||
.map(|item| PathChunk::Name(item.to_string()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
impl From<&[PathChunk]> for JSONPointer {
|
||||
#[inline]
|
||||
fn from(path: &[PathChunk]) -> Self {
|
||||
JSONPointer(path.to_vec())
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{compilation::JSONSchema, error::ErrorIterator, keywords::InstancePath};
|
||||
use crate::{compilation::JSONSchema, error::ErrorIterator, paths::InstancePath};
|
||||
use serde_json::Value;
|
||||
use std::fmt;
|
||||
|
||||
|
|
|
@ -44,11 +44,7 @@ fn test_draft(_server_address: &str, test_case: TestCase) {
|
|||
);
|
||||
let errors: Vec<_> = result.expect_err("Errors").collect();
|
||||
for error in errors {
|
||||
let pointer = if error.instance_path.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!("/{}", error.instance_path.join("/"))
|
||||
};
|
||||
let pointer = error.instance_path.to_string();
|
||||
assert_eq!(test_case.instance.pointer(&pointer), Some(&*error.instance))
|
||||
}
|
||||
}
|
||||
|
@ -92,9 +88,12 @@ fn test_instance_path() {
|
|||
.next()
|
||||
.expect("Validation error");
|
||||
assert_eq!(
|
||||
error.instance_path, instance_path,
|
||||
error.instance_path.into_vec(),
|
||||
instance_path,
|
||||
"File: {}; Suite ID: {}; Test ID: {}",
|
||||
filename, suite_id, test_id
|
||||
filename,
|
||||
suite_id,
|
||||
test_id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue