Split Visitor into two traits
This commit is contained in:
parent
656a70e02c
commit
a02947462d
3 changed files with 121 additions and 74 deletions
|
@ -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]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue