valico/json_schema/keywords/
ref_.rs1use 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}