valico/json_schema/keywords/
ref_.rs

1use serde_json::Value;
2use url::Url;
3
4use crate::json_schema::SchemaVersion;
5
6use super::super::schema;
7use super::super::validators;
8
9#[allow(missing_copy_implementations)]
10pub struct Ref;
11impl super::Keyword for Ref {
12    fn compile(&self, def: &Value, ctx: &schema::WalkContext<'_>) -> super::KeywordResult {
13        let ref_ = keyword_key_exists!(def, "$ref");
14
15        if ref_.is_string() {
16            let url = Url::options()
17                .base_url(Some(ctx.url))
18                .parse(ref_.as_str().unwrap());
19            match url {
20                Ok(url) => Ok(Some(Box::new(validators::Ref { url }))),
21                Err(_) => Err(schema::SchemaError::Malformed {
22                    path: ctx.fragment.join("/"),
23                    detail: "The value of $ref MUST be an URI-encoded JSON Pointer".to_string(),
24                }),
25            }
26        } else {
27            Err(schema::SchemaError::Malformed {
28                path: ctx.fragment.join("/"),
29                detail: "The value of multipleOf MUST be a string".to_string(),
30            })
31        }
32    }
33
34    fn is_exclusive(&self, version: SchemaVersion) -> bool {
35        version < SchemaVersion::Draft2019_09
36    }
37}
38
39#[cfg(test)]
40use super::super::builder;
41#[cfg(test)]
42use super::super::scope;
43#[cfg(test)]
44use serde_json::to_value;
45
46#[cfg(test)]
47fn mk_schema() -> Value {
48    json!({
49        "a": { "default": 42 },
50        "b": { "properties": { "x": { "default": [1,2,3] } } },
51        "properties": {
52            "y": { "$ref": "#/a" },
53            "z": { "$ref": "#/b" }
54        },
55        "required": [ "y", "z" ]
56    })
57}
58
59#[test]
60fn default_for_schema() {
61    let mut scope = scope::Scope::new().supply_defaults();
62    let schema = scope.compile_and_return(mk_schema(), false).unwrap();
63    assert_eq!(
64        schema.get_default(),
65        Some(json!({"y": 42, "z": {"x": [1,2,3]}}))
66    );
67}
68
69#[test]
70fn default_when_needed() {
71    let mut scope = scope::Scope::new().supply_defaults();
72    let schema = scope.compile_and_return(mk_schema(), false).unwrap();
73    let result = schema.validate(&json!({"x": true}));
74    assert!(result.is_strictly_valid());
75    assert_eq!(
76        result.replacement,
77        Some(json!({"x": true, "y": 42, "z": {"x": [1,2,3]}}))
78    );
79}
80
81#[test]
82fn no_default_otherwise() {
83    let mut scope = scope::Scope::new().supply_defaults();
84    let schema = scope.compile_and_return(mk_schema(), false).unwrap();
85    let result = schema.validate(&json!({"y": null, "z": {"x":false}}));
86    assert!(result.is_strictly_valid());
87    assert_eq!(result.replacement, None);
88}
89
90#[test]
91fn validate() {
92    let mut scope = scope::Scope::new();
93    let schema = scope
94        .compile_and_return(
95            builder::schema(|s| {
96                s.array();
97                s.max_items(2u64);
98                s.items_schema(|items| {
99                    items.ref_("#");
100                })
101            })
102            .into_json(),
103            true,
104        )
105        .ok()
106        .unwrap();
107
108    let array: Vec<String> = vec![];
109    let array2: Vec<Vec<String>> = vec![vec![], vec![]];
110    let array3: Vec<Vec<String>> = vec![vec![], vec![], vec![]];
111
112    assert_eq!(schema.validate(&to_value(array).unwrap()).is_valid(), true);
113    assert_eq!(schema.validate(&to_value(array2).unwrap()).is_valid(), true);
114
115    assert_eq!(
116        schema.validate(&to_value(array3).unwrap()).is_valid(),
117        false
118    );
119    assert_eq!(
120        schema.validate(&to_value(vec![1, 2]).unwrap()).is_valid(),
121        false
122    );
123}