diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 01f2f00..0227d06 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -4,19 +4,90 @@ use std::collections::BTreeMap as Map; use std::collections::BTreeSet as Set; use std::iter::FromIterator; -#[derive(Debug, Default)] +#[derive(Debug, PartialEq, Clone)] +pub struct SchemaSettings { + pub option_nullable: bool, + pub option_any_of_null: bool, + pub bool_schemas: BoolSchemas, + pub definitions_path: String, +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum BoolSchemas { + Enable, + AdditionalPropertiesOnly, + Disable, +} + +impl Default for SchemaSettings { + fn default() -> SchemaSettings { + SchemaSettings { + option_nullable: false, + option_any_of_null: true, + bool_schemas: BoolSchemas::Enable, + definitions_path: "#/definitions/".to_owned(), + } + } +} + +impl SchemaSettings { + pub fn new() -> SchemaSettings { + SchemaSettings { + ..Default::default() + } + } + pub fn openapi3() -> SchemaSettings { + SchemaSettings { + option_nullable: true, + option_any_of_null: false, + bool_schemas: BoolSchemas::AdditionalPropertiesOnly, + definitions_path: "#/components/schemas/".to_owned(), + } + } + + pub fn into_generator(self) -> SchemaGenerator { + SchemaGenerator::new(self) + } +} + +#[derive(Debug, Default, Clone)] pub struct SchemaGenerator { + settings: SchemaSettings, names: Set, definitions: Map, } impl SchemaGenerator { - pub fn new() -> SchemaGenerator { + pub fn new(settings: SchemaSettings) -> SchemaGenerator { SchemaGenerator { + settings, ..Default::default() } } + pub fn settings(&self) -> &SchemaSettings { + &self.settings + } + + pub fn schema_for_any(&self) -> Schema { + if self.settings().bool_schemas == BoolSchemas::Enable { + true.into() + } else { + Schema::Object(Default::default()) + } + } + + pub fn schema_for_none(&self) -> Schema { + if self.settings().bool_schemas == BoolSchemas::Enable { + false.into() + } else { + Schema::Object(SchemaObject { + not: Some(Schema::Object(Default::default()).into()), + ..Default::default() + }) + } + } + pub fn subschema_for(&mut self) -> Schema { if !T::generates_ref_schema() { return T::make_schema(self); @@ -33,7 +104,7 @@ impl SchemaGenerator { self.insert_new_subschema_for::(type_id, name.clone()); name }); - let reference = format!("#/definitions/{}", name); + let reference = format!("{}{}", self.settings().definitions_path, name); SchemaRef { reference }.into() } diff --git a/schemars/src/macros.rs b/schemars/src/macros.rs index 83bdcd2..6d19e51 100644 --- a/schemars/src/macros.rs +++ b/schemars/src/macros.rs @@ -1,6 +1,6 @@ #[macro_export()] macro_rules! schema_for { ($($type:tt)+) => { - $crate::gen::SchemaGenerator::new().into_root_schema_for::<$($type)+>() + $crate::gen::SchemaGenerator::default().into_root_schema_for::<$($type)+>() }; } diff --git a/schemars/src/make_schema.rs b/schemars/src/make_schema.rs index 1e2f741..2b5a3bb 100644 --- a/schemars/src/make_schema.rs +++ b/schemars/src/make_schema.rs @@ -1,4 +1,4 @@ -use crate::gen::SchemaGenerator; +use crate::gen::{BoolSchemas, SchemaGenerator}; use crate::schema::*; use serde_json::json; use std::collections::BTreeMap as Map; @@ -233,10 +233,17 @@ macro_rules! map_impl { fn make_schema(gen: &mut SchemaGenerator) -> Schema { + let subschema = gen.subschema_for::(); + let make_schema_bool = gen.settings().bool_schemas == BoolSchemas::AdditionalPropertiesOnly + && subschema == gen.schema_for_any(); let mut extensions = Map::new(); extensions.insert( "additionalProperties".to_owned(), - json!(gen.subschema_for::()) + if make_schema_bool { + json!(true) + } else { + json!(subschema) + } ); SchemaObject { instance_type: Some(InstanceType::Object.into()), @@ -257,15 +264,31 @@ impl MakeSchema for Option { no_ref_schema!(); fn make_schema(gen: &mut SchemaGenerator) -> Schema { - match gen.subschema_for::() { + let settings = gen.settings(); + let make_any_of = settings.option_any_of_null; + let set_nullable = settings.option_nullable; + let mut schema = match gen.subschema_for::() { Schema::Bool(true) => true.into(), Schema::Bool(false) => <()>::make_schema(gen), - schema => SchemaObject { - any_of: Some(vec![schema, <()>::make_schema(gen)]), - ..Default::default() + schema => { + if make_any_of { + SchemaObject { + any_of: Some(vec![schema, <()>::make_schema(gen)]), + ..Default::default() + } + .into() + } else { + schema + } } - .into(), - } + }; + if set_nullable { + // FIXME still need to handle ref schemas here + if let Schema::Object(ref mut o) = schema { + o.extensions.insert("nullable".to_owned(), true.into()); + } + }; + schema } } @@ -298,7 +321,7 @@ deref_impl!(<'a, T: ToOwned> MakeSchema for std::borrow::Cow<'a, T>); impl MakeSchema for serde_json::Value { no_ref_schema!(); - fn make_schema(_: &mut SchemaGenerator) -> Schema { - true.into() + fn make_schema(gen: &mut SchemaGenerator) -> Schema { + gen.schema_for_any() } }