vector_config_common/schema/
gen.rs

1use super::{visit::Visitor, Map, RootSchema, Schema, SchemaObject, DEFINITIONS_PREFIX};
2
3/// Settings to customize how schemas are generated.
4#[derive(Debug)]
5pub struct SchemaSettings {
6    /// A JSON pointer to the expected location of referenceable subschemas within the resulting root schema.
7    definitions_path: String,
8
9    /// The URI of the meta-schema describing the structure of the generated schemas.
10    meta_schema: String,
11
12    /// A list of visitors that get applied to all generated root schemas.
13    visitors: Vec<Box<dyn Visitor>>,
14}
15
16impl Default for SchemaSettings {
17    fn default() -> SchemaSettings {
18        SchemaSettings::new()
19    }
20}
21
22impl SchemaSettings {
23    /// Creates `SchemaSettings` that conform to [JSON Schema 2019-09][json_schema_2019_09].
24    ///
25    /// [json_schema_2019_09]: https://json-schema.org/specification-links.html#2019-09-formerly-known-as-draft-8
26    pub fn new() -> SchemaSettings {
27        SchemaSettings {
28            definitions_path: DEFINITIONS_PREFIX.to_owned(),
29            meta_schema: "https://json-schema.org/draft/2019-09/schema".to_string(),
30            visitors: Vec::default(),
31        }
32    }
33
34    /// Gets the definitions path used by this generator.
35    pub fn definitions_path(&self) -> &str {
36        &self.definitions_path
37    }
38
39    /// Creates a `Visitor` from the given closure and appends it to the list of
40    /// [visitors](SchemaSettings::visitors) for these `SchemaSettings`.
41    #[allow(rustdoc::private_intra_doc_links)]
42    pub fn with_visitor<F, V>(mut self, visitor_fn: F) -> Self
43    where
44        F: FnOnce(&Self) -> V,
45        V: Visitor + 'static,
46    {
47        let visitor = visitor_fn(&self);
48        self.visitors.push(Box::new(visitor));
49        self
50    }
51
52    /// Creates a new [`SchemaGenerator`] using these settings.
53    pub fn into_generator(self) -> SchemaGenerator {
54        SchemaGenerator::new(self)
55    }
56}
57
58/// Schema generator.
59///
60/// This is the main entrypoint for storing the defined schemas within a given root schema, and
61/// referencing existing schema definitions.
62#[derive(Debug, Default)]
63pub struct SchemaGenerator {
64    settings: SchemaSettings,
65    definitions: Map<String, Schema>,
66}
67
68impl From<SchemaSettings> for SchemaGenerator {
69    fn from(settings: SchemaSettings) -> Self {
70        settings.into_generator()
71    }
72}
73
74impl SchemaGenerator {
75    /// Creates a new `SchemaGenerator` using the given settings.
76    pub fn new(settings: SchemaSettings) -> SchemaGenerator {
77        SchemaGenerator {
78            settings,
79            ..Default::default()
80        }
81    }
82
83    /// Gets the [`SchemaSettings`] being used by this `SchemaGenerator`.
84    pub fn settings(&self) -> &SchemaSettings {
85        &self.settings
86    }
87
88    /// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that
89    /// have been generated.
90    ///
91    /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the
92    /// values are the schemas themselves.
93    #[allow(rustdoc::broken_intra_doc_links)]
94    pub fn definitions(&self) -> &Map<String, Schema> {
95        &self.definitions
96    }
97
98    /// Mutably borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas
99    /// that have been generated.
100    ///
101    /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the
102    /// values are the schemas themselves.
103    #[allow(rustdoc::broken_intra_doc_links)]
104    pub fn definitions_mut(&mut self) -> &mut Map<String, Schema> {
105        &mut self.definitions
106    }
107
108    /// Attempts to find the schema that the given `schema` is referencing.
109    ///
110    /// If the given `schema` has a [`$ref`](../schema/struct.SchemaObject.html#structfield.reference)
111    /// property which refers to another schema in `self`'s schema definitions, the referenced
112    /// schema will be returned.  Otherwise, returns `None`.
113    pub fn dereference<'a>(&'a self, schema: &Schema) -> Option<&'a Schema> {
114        match schema {
115            Schema::Object(SchemaObject {
116                reference: Some(ref schema_ref),
117                ..
118            }) => {
119                let definitions_path = &self.settings().definitions_path;
120                if schema_ref.starts_with(definitions_path) {
121                    let name = &schema_ref[definitions_path.len()..];
122                    self.definitions.get(name)
123                } else {
124                    None
125                }
126            }
127            _ => None,
128        }
129    }
130
131    /// Converts this generator into a root schema, using the given `root_schema` as the top-level
132    /// definition.
133    ///
134    /// This assumes the root schema was generated using this generator, such that any schema
135    /// definitions referenced by `root_schema` refer to this generator.
136    ///
137    /// All other relevant settings (i.e. meta-schema) are carried over.
138    pub fn into_root_schema(mut self, root_schema: SchemaObject) -> RootSchema {
139        let mut root_schema = RootSchema {
140            meta_schema: Some(self.settings.meta_schema),
141            schema: root_schema,
142            definitions: self.definitions,
143        };
144
145        for visitor in self.settings.visitors.iter_mut() {
146            visitor.visit_root_schema(&mut root_schema);
147        }
148
149        root_schema
150    }
151}