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