Split Visitor into two traits

This commit is contained in:
Graham Esau 2020-06-07 21:46:08 +01:00
parent 656a70e02c
commit a02947462d
3 changed files with 121 additions and 74 deletions

View file

@ -1,6 +1,8 @@
use crate::flatten::Merge; use crate::flatten::Merge;
use crate::schema::*; use crate::schema::*;
use crate::{visit::*, JsonSchema, Map}; use crate::{visit::*, JsonSchema, Map};
use dyn_clone::DynClone;
use std::{any::Any, fmt::Debug};
/// Settings to customize how Schemas are generated. /// Settings to customize how Schemas are generated.
/// ///
@ -26,8 +28,8 @@ pub struct SchemaSettings {
/// ///
/// Defaults to `"http://json-schema.org/draft-07/schema#"`. /// Defaults to `"http://json-schema.org/draft-07/schema#"`.
pub meta_schema: Option<String>, pub meta_schema: Option<String>,
/// TODO document /// A list of visitors that get applied to all generated root schemas.
pub visitors: Visitors, pub visitors: Vec<Box<dyn Visitor2>>,
_hidden: (), _hidden: (),
} }
@ -45,7 +47,7 @@ impl SchemaSettings {
option_add_null_type: true, option_add_null_type: true,
definitions_path: "#/definitions/".to_owned(), definitions_path: "#/definitions/".to_owned(),
meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()), meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()),
visitors: Visitors(vec![Box::new(RemoveRefSiblings)]), visitors: vec![Box::new(RemoveRefSiblings)],
_hidden: (), _hidden: (),
} }
} }
@ -57,7 +59,7 @@ impl SchemaSettings {
option_add_null_type: true, option_add_null_type: true,
definitions_path: "#/definitions/".to_owned(), definitions_path: "#/definitions/".to_owned(),
meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()), meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()),
visitors: Visitors::default(), visitors: Vec::default(),
_hidden: (), _hidden: (),
} }
} }
@ -72,12 +74,12 @@ impl SchemaSettings {
"https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema" "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema"
.to_owned(), .to_owned(),
), ),
visitors: Visitors(vec![ visitors: vec![
Box::new(RemoveRefSiblings), Box::new(RemoveRefSiblings),
Box::new(ReplaceBoolSchemas { Box::new(ReplaceBoolSchemas {
skip_additional_properties: true, skip_additional_properties: true,
}), }),
]), ],
_hidden: (), _hidden: (),
} }
} }
@ -99,9 +101,9 @@ impl SchemaSettings {
self self
} }
/// TODO document /// Appends the given visitor to the list of [visitors](SchemaSettings::visitors) for these `SchemaSettings`.
pub fn with_visitor(mut self, visitor: impl Visitor + 'static) -> Self { pub fn with_visitor(mut self, visitor: impl Visitor + Debug + DynClone + 'static) -> Self {
self.visitors.0.push(Box::new(visitor)); self.visitors.push(Box::new(visitor));
self self
} }
@ -111,10 +113,6 @@ impl SchemaSettings {
} }
} }
/// TODO document
#[derive(Debug, Clone, Default)]
pub struct Visitors(Vec<Box<dyn Visitor>>);
/// The main type used to generate JSON Schemas. /// The main type used to generate JSON Schemas.
/// ///
/// # Example /// # Example
@ -236,7 +234,7 @@ impl SchemaGenerator {
schema, schema,
}; };
for visitor in &mut self.settings.visitors.0 { for visitor in &mut self.settings.visitors {
visitor.visit_root_schema(&mut root) visitor.visit_root_schema(&mut root)
} }
@ -256,7 +254,7 @@ impl SchemaGenerator {
schema, schema,
}; };
for visitor in &mut self.settings.visitors.0 { for visitor in &mut self.settings.visitors {
visitor.visit_root_schema(&mut root) visitor.visit_root_schema(&mut root)
} }
@ -323,62 +321,39 @@ impl SchemaGenerator {
} }
} }
/// TODO document /// A [Visitor](Visitor) which implements additional traits required to be included in a [SchemaSettings].
#[derive(Debug, Clone)] ///
pub struct ReplaceBoolSchemas { /// You will rarely need to use this trait directly as it is automatically implemented for any type which implements all of:
pub skip_additional_properties: bool, /// - [`Visitor`]
/// - [`std::fmt::Debug`]
/// - [`std::any::Any`] (implemented for all `'static` types)
/// - [`dyn_clone::DynClone`] (or [`std::clone::Clone`])
///
/// # Example
/// ```
/// use schemars::visit::Visitor;
/// use schemars::gen::Visitor2;
///
/// #[derive(Debug, Clone)]
/// struct MyVisitor;
///
/// impl Visitor for MyVisitor { }
///
/// let v: &dyn Visitor2 = &MyVisitor;
/// assert_eq!(v.as_any().is::<MyVisitor>(), true);
/// ```
pub trait Visitor2: Visitor + Debug + DynClone + Any {
/// Upcasts this visitor into an `Any`, which can be used to inspect and manipulate it as its concrete type.
fn as_any(&self) -> &dyn Any;
} }
impl Visitor for ReplaceBoolSchemas { dyn_clone::clone_trait_object!(Visitor2);
fn visit_schema(&mut self, schema: &mut Schema) {
if let Schema::Bool(b) = *schema {
*schema = Schema::Bool(b).into_object().into()
}
visit_schema(self, schema) impl<T> Visitor2 for T
} where
T: Visitor + Debug + DynClone + Any,
fn visit_schema_object(&mut self, schema: &mut SchemaObject) { {
if self.skip_additional_properties { fn as_any(&self) -> &dyn Any {
let mut additional_properties = None; self
if let Some(obj) = &mut schema.object {
if let Some(ap) = &obj.additional_properties {
if let Schema::Bool(_) = ap.as_ref() {
additional_properties = obj.additional_properties.take();
}
}
}
visit_schema_object(self, schema);
if additional_properties.is_some() {
schema.object().additional_properties = additional_properties;
}
} else {
visit_schema_object(self, schema);
}
}
}
/// TODO document
#[derive(Debug, Clone)]
pub struct RemoveRefSiblings;
impl Visitor for RemoveRefSiblings {
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
visit_schema_object(self, schema);
if let Some(reference) = schema.reference.take() {
if schema == &SchemaObject::default() {
schema.reference = Some(reference);
} else {
let ref_schema = Schema::new_ref(reference);
let all_of = &mut schema.subschemas().all_of;
match all_of {
Some(vec) => vec.push(ref_schema),
None => *all_of = Some(vec![ref_schema]),
}
}
}
} }
} }

View file

@ -41,7 +41,18 @@ impl Schema {
} }
} }
/// TODO document /// Converts the given schema (if it is a boolean schema) into an equivalent schema object.
///
/// If the given schema is already a schema object, this has no effect.
///
/// # Example
/// ```
/// use schemars::schema::{Schema, SchemaObject};
///
/// let bool_schema = Schema::Bool(true);
///
/// assert_eq!(bool_schema.into_object(), SchemaObject::default());
/// ```
pub fn into_object(self) -> SchemaObject { pub fn into_object(self) -> SchemaObject {
match self { match self {
Schema::Object(o) => o, Schema::Object(o) => o,

View file

@ -1,34 +1,38 @@
use crate::schema::{RootSchema, Schema, SchemaObject, SingleOrVec}; use crate::schema::{RootSchema, Schema, SchemaObject, SingleOrVec};
use dyn_clone::DynClone;
use std::fmt::Debug; use std::fmt::Debug;
pub trait Visitor: Debug + DynClone { /// TODO document
pub trait Visitor {
/// TODO document
fn visit_root_schema(&mut self, root: &mut RootSchema) { fn visit_root_schema(&mut self, root: &mut RootSchema) {
visit_root_schema(self, root) visit_root_schema(self, root)
} }
/// TODO document
fn visit_schema(&mut self, schema: &mut Schema) { fn visit_schema(&mut self, schema: &mut Schema) {
visit_schema(self, schema) visit_schema(self, schema)
} }
/// TODO document
fn visit_schema_object(&mut self, schema: &mut SchemaObject) { fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
visit_schema_object(self, schema) visit_schema_object(self, schema)
} }
} }
dyn_clone::clone_trait_object!(Visitor); /// TODO document
pub fn visit_root_schema<V: Visitor + ?Sized>(v: &mut V, root: &mut RootSchema) { pub fn visit_root_schema<V: Visitor + ?Sized>(v: &mut V, root: &mut RootSchema) {
v.visit_schema_object(&mut root.schema); v.visit_schema_object(&mut root.schema);
visit_map_values(v, &mut root.definitions); visit_map_values(v, &mut root.definitions);
} }
/// TODO document
pub fn visit_schema<V: Visitor + ?Sized>(v: &mut V, schema: &mut Schema) { pub fn visit_schema<V: Visitor + ?Sized>(v: &mut V, schema: &mut Schema) {
if let Schema::Object(schema) = schema { if let Schema::Object(schema) = schema {
v.visit_schema_object(schema) v.visit_schema_object(schema)
} }
} }
/// TODO document
pub fn visit_schema_object<V: Visitor + ?Sized>(v: &mut V, schema: &mut SchemaObject) { pub fn visit_schema_object<V: Visitor + ?Sized>(v: &mut V, schema: &mut SchemaObject) {
if let Some(sub) = &mut schema.subschemas { if let Some(sub) = &mut schema.subschemas {
visit_vec(v, &mut sub.all_of); visit_vec(v, &mut sub.all_of);
@ -85,3 +89,60 @@ fn visit_single_or_vec<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<Singl
} }
} }
} }
/// TODO document
#[derive(Debug, Clone)]
pub struct ReplaceBoolSchemas {
pub skip_additional_properties: bool,
}
impl Visitor for ReplaceBoolSchemas {
fn visit_schema(&mut self, schema: &mut Schema) {
visit_schema(self, schema);
if let Schema::Bool(b) = *schema {
*schema = Schema::Bool(b).into_object().into()
}
}
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
if self.skip_additional_properties {
if let Some(obj) = &mut schema.object {
if let Some(ap) = &obj.additional_properties {
if let Schema::Bool(_) = ap.as_ref() {
let additional_properties = obj.additional_properties.take();
visit_schema_object(self, schema);
schema.object().additional_properties = additional_properties;
return;
}
}
}
}
visit_schema_object(self, schema);
}
}
/// TODO document
#[derive(Debug, Clone)]
pub struct RemoveRefSiblings;
impl Visitor for RemoveRefSiblings {
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
visit_schema_object(self, schema);
if let Some(reference) = schema.reference.take() {
if schema == &SchemaObject::default() {
schema.reference = Some(reference);
} else {
let ref_schema = Schema::new_ref(reference);
let all_of = &mut schema.subschemas().all_of;
match all_of {
Some(vec) => vec.push(ref_schema),
None => *all_of = Some(vec![ref_schema]),
}
}
}
}
}