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}