valico/json_schema/
scope.rs

1use url;
2use std::collections;
3use serde_json::{Value};
4
5use super::schema;
6use super::keywords;
7use super::helpers;
8
9#[allow(dead_code)]
10#[derive(Debug)]
11pub struct Scope {
12    keywords: keywords::KeywordMap,
13    schemes: collections::HashMap<String, schema::Schema>,
14}
15
16#[allow(dead_code)]
17impl Scope {
18    pub fn new() -> Scope {
19        let mut scope = Scope {
20            keywords: keywords::default(),
21            schemes: collections::HashMap::new()
22        };
23
24        scope.add_keyword(vec!["format"], keywords::format::Format::new());
25        scope
26    }
27
28    pub fn without_formats() -> Scope {
29        Scope {
30            keywords: keywords::default(),
31            schemes: collections::HashMap::new()
32        }
33    }
34
35    pub fn with_formats<F>(build_formats: F) -> Scope where F: FnOnce(&mut keywords::format::FormatBuilders) {
36        let mut scope = Scope {
37            keywords: keywords::default(),
38            schemes: collections::HashMap::new()
39        };
40
41        scope.add_keyword(vec!["format"], keywords::format::Format::with(build_formats));
42        scope
43    }
44
45    pub fn compile(&mut self, def: Value, ban_unknown: bool) -> Result<url::Url, schema::SchemaError> {
46        let schema = try!(schema::compile(def, None, schema::CompilationSettings::new(&self.keywords, ban_unknown)));
47        let id = schema.id.clone().unwrap();
48        try!(self.add(&id, schema));
49        Ok(id)
50    }
51
52    pub fn compile_with_id(&mut self, id: &url::Url, def: Value, ban_unknown: bool)
53        -> Result<(), schema::SchemaError>
54    {
55        let schema = try!(schema::compile(def, Some(id.clone()), schema::CompilationSettings::new(&self.keywords, ban_unknown)));
56        self.add(id, schema)
57    }
58
59    pub fn compile_and_return<'a>(&'a mut self, def: Value, ban_unknown: bool)
60        -> Result<schema::ScopedSchema<'a>, schema::SchemaError>
61    {
62        let schema = try!(schema::compile(def, None, schema::CompilationSettings::new(&self.keywords, ban_unknown)));
63        self.add_and_return(schema.id.clone().as_ref().unwrap(), schema)
64    }
65
66    pub fn compile_and_return_with_id<'a>(&'a mut self, id: &url::Url, def: Value, ban_unknown: bool)
67        -> Result<schema::ScopedSchema<'a>, schema::SchemaError>
68    {
69        let schema = try!(schema::compile(def, Some(id.clone()), schema::CompilationSettings::new(&self.keywords, ban_unknown)));
70        self.add_and_return(id, schema)
71    }
72
73    pub fn add_keyword<T>(&mut self, keys: Vec<&'static str>, keyword: T) where T: keywords::Keyword + 'static {
74        keywords::decouple_keyword((keys, Box::new(keyword)), &mut self.keywords);
75    }
76
77    fn add(&mut self, id: &url::Url, schema: schema::Schema) -> Result<(), schema::SchemaError> {
78        let (id_str, fragment) = helpers::serialize_schema_path(id);
79
80        match fragment {
81            Some(_) => return Err(schema::SchemaError::WrongId),
82            None => ()
83        }
84
85        if !self.schemes.contains_key(&id_str) {
86            self.schemes.insert(id_str, schema);
87            Ok(())
88        } else {
89            Err(schema::SchemaError::IdConflicts)
90        }
91    }
92
93    fn add_and_return<'a>(&'a mut self, id: &url::Url, schema: schema::Schema)
94        -> Result<schema::ScopedSchema<'a>, schema::SchemaError>
95    {
96        let (id_str, fragment) = helpers::serialize_schema_path(id);
97
98        match fragment {
99            Some(_) => return Err(schema::SchemaError::WrongId),
100            None => ()
101        }
102
103        if !self.schemes.contains_key(&id_str) {
104            self.schemes.insert(id_str.clone(), schema);
105            Ok(schema::ScopedSchema::new(self, self.schemes.get(&id_str).unwrap()))
106        } else {
107            Err(schema::SchemaError::IdConflicts)
108        }
109    }
110
111    pub fn resolve<'a>(&'a self, id: &url::Url) -> Option<schema::ScopedSchema<'a>> {
112        let (schema_path, fragment) = helpers::serialize_schema_path(id);
113
114        let schema = self.schemes.get(&schema_path).or_else(|| {
115            // Searching for inline schema in O(N)
116            for (_, schema) in self.schemes.iter() {
117                let internal_schema = schema.resolve(schema_path.as_ref());
118                if internal_schema.is_some() {
119                    return internal_schema
120                }
121            }
122
123            None
124        });
125
126        schema.and_then(|schema| {
127            match fragment {
128                Some(ref fragment) => {
129                    schema.resolve_fragment(fragment.as_ref()).map(|schema| {
130                        schema::ScopedSchema::new(self, schema)
131                    })
132                },
133                None => Some(schema::ScopedSchema::new(self, schema))
134            }
135        })
136    }
137}
138
139#[cfg(test)]
140use jsonway;
141
142#[test]
143fn lookup() {
144    let mut scope = Scope::new();
145
146    scope.compile(jsonway::object(|schema| {
147        schema.set("id", "http://example.com/schema".to_string())
148    }).unwrap(), false).ok().unwrap();
149
150    scope.compile(jsonway::object(|schema| {
151        schema.set("id", "http://example.com/schema#sub".to_string());
152        schema.object("subschema", |subschema| {
153            subschema.set("id", "#subschema".to_string());
154        })
155    }).unwrap(), false).ok().unwrap();
156
157    assert!(scope.resolve(&url::Url::parse("http://example.com/schema").ok().unwrap()).is_some());
158    assert!(scope.resolve(&url::Url::parse("http://example.com/schema#sub").ok().unwrap()).is_some());
159    assert!(scope.resolve(&url::Url::parse("http://example.com/schema#sub/subschema").ok().unwrap()).is_some());
160    assert!(scope.resolve(&url::Url::parse("http://example.com/schema#subschema").ok().unwrap()).is_some());
161}