boon/
roots.rs

1use std::collections::{HashMap, HashSet};
2
3use crate::{compiler::CompileError, draft::*, loader::DefaultUrlLoader, root::Root, util::*};
4
5use serde_json::Value;
6use url::Url;
7
8// --
9
10pub(crate) struct Roots {
11    pub(crate) default_draft: &'static Draft,
12    map: HashMap<Url, Root>,
13    pub(crate) loader: DefaultUrlLoader,
14}
15
16impl Roots {
17    fn new() -> Self {
18        Self {
19            default_draft: latest(),
20            map: Default::default(),
21            loader: DefaultUrlLoader::new(),
22        }
23    }
24}
25
26impl Default for Roots {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl Roots {
33    pub(crate) fn get(&self, url: &Url) -> Option<&Root> {
34        self.map.get(url)
35    }
36
37    pub(crate) fn resolve_fragment(&mut self, uf: UrlFrag) -> Result<UrlPtr, CompileError> {
38        self.or_load(uf.url.clone())?;
39        let Some(root) = self.map.get(&uf.url) else {
40            return Err(CompileError::Bug("or_load didn't add".into()));
41        };
42        root.resolve_fragment(&uf.frag)
43    }
44
45    pub(crate) fn ensure_subschema(&mut self, up: &UrlPtr) -> Result<(), CompileError> {
46        self.or_load(up.url.clone())?;
47        let Some(root) = self.map.get_mut(&up.url) else {
48            return Err(CompileError::Bug("or_load didn't add".into()));
49        };
50        if !root.draft.is_subschema(up.ptr.as_str()) {
51            let doc = self.loader.load(&root.url)?;
52            let v = up.ptr.lookup(doc, &up.url)?;
53            root.draft.validate(up, v)?;
54            root.add_subschema(doc, &up.ptr)?;
55        }
56        Ok(())
57    }
58
59    pub(crate) fn or_load(&mut self, url: Url) -> Result<(), CompileError> {
60        debug_assert!(url.fragment().is_none(), "trying to add root with fragment");
61        if self.map.contains_key(&url) {
62            return Ok(());
63        }
64        let doc = self.loader.load(&url)?;
65        let r = self.create_root(url.clone(), doc)?;
66        self.map.insert(url, r);
67        Ok(())
68    }
69
70    pub(crate) fn create_root(&self, url: Url, doc: &Value) -> Result<Root, CompileError> {
71        let draft = {
72            let up = UrlPtr {
73                url: url.clone(),
74                ptr: "".into(),
75            };
76            self.loader
77                .get_draft(&up, doc, self.default_draft, HashSet::new())?
78        };
79        let vocabs = self.loader.get_meta_vocabs(doc, draft)?;
80        let resources = {
81            let mut m = HashMap::default();
82            draft.collect_resources(doc, &url, "".into(), &url, &mut m)?;
83            m
84        };
85
86        if !matches!(url.host_str(), Some("json-schema.org")) {
87            draft.validate(
88                &UrlPtr {
89                    url: url.clone(),
90                    ptr: "".into(),
91                },
92                doc,
93            )?;
94        }
95
96        Ok(Root {
97            draft,
98            resources,
99            url: url.clone(),
100            meta_vocabs: vocabs,
101        })
102    }
103
104    pub(crate) fn insert(&mut self, roots: &mut HashMap<Url, Root>) {
105        self.map.extend(roots.drain());
106    }
107}