Add some config for openapi3 compatibility

This commit is contained in:
Graham Esau 2019-08-05 23:30:53 +01:00
parent ef5c584118
commit 1f514f2be7
3 changed files with 108 additions and 14 deletions

View file

@ -4,19 +4,90 @@ use std::collections::BTreeMap as Map;
use std::collections::BTreeSet as Set; use std::collections::BTreeSet as Set;
use std::iter::FromIterator; 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 { pub struct SchemaGenerator {
settings: SchemaSettings,
names: Set<String>, names: Set<String>,
definitions: Map<SchemaTypeId, (String, Schema)>, definitions: Map<SchemaTypeId, (String, Schema)>,
} }
impl SchemaGenerator { impl SchemaGenerator {
pub fn new() -> SchemaGenerator { pub fn new(settings: SchemaSettings) -> SchemaGenerator {
SchemaGenerator { SchemaGenerator {
settings,
..Default::default() ..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<T: ?Sized + MakeSchema>(&mut self) -> Schema { pub fn subschema_for<T: ?Sized + MakeSchema>(&mut self) -> Schema {
if !T::generates_ref_schema() { if !T::generates_ref_schema() {
return T::make_schema(self); return T::make_schema(self);
@ -33,7 +104,7 @@ impl SchemaGenerator {
self.insert_new_subschema_for::<T>(type_id, name.clone()); self.insert_new_subschema_for::<T>(type_id, name.clone());
name name
}); });
let reference = format!("#/definitions/{}", name); let reference = format!("{}{}", self.settings().definitions_path, name);
SchemaRef { reference }.into() SchemaRef { reference }.into()
} }

View file

@ -1,6 +1,6 @@
#[macro_export()] #[macro_export()]
macro_rules! schema_for { macro_rules! schema_for {
($($type:tt)+) => { ($($type:tt)+) => {
$crate::gen::SchemaGenerator::new().into_root_schema_for::<$($type)+>() $crate::gen::SchemaGenerator::default().into_root_schema_for::<$($type)+>()
}; };
} }

View file

@ -1,4 +1,4 @@
use crate::gen::SchemaGenerator; use crate::gen::{BoolSchemas, SchemaGenerator};
use crate::schema::*; use crate::schema::*;
use serde_json::json; use serde_json::json;
use std::collections::BTreeMap as Map; use std::collections::BTreeMap as Map;
@ -233,10 +233,17 @@ macro_rules! map_impl {
fn make_schema(gen: &mut SchemaGenerator) -> Schema fn make_schema(gen: &mut SchemaGenerator) -> Schema
{ {
let subschema = gen.subschema_for::<V>();
let make_schema_bool = gen.settings().bool_schemas == BoolSchemas::AdditionalPropertiesOnly
&& subschema == gen.schema_for_any();
let mut extensions = Map::new(); let mut extensions = Map::new();
extensions.insert( extensions.insert(
"additionalProperties".to_owned(), "additionalProperties".to_owned(),
json!(gen.subschema_for::<V>()) if make_schema_bool {
json!(true)
} else {
json!(subschema)
}
); );
SchemaObject { SchemaObject {
instance_type: Some(InstanceType::Object.into()), instance_type: Some(InstanceType::Object.into()),
@ -257,15 +264,31 @@ impl<T: MakeSchema> MakeSchema for Option<T> {
no_ref_schema!(); no_ref_schema!();
fn make_schema(gen: &mut SchemaGenerator) -> Schema { fn make_schema(gen: &mut SchemaGenerator) -> Schema {
match gen.subschema_for::<T>() { 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::<T>() {
Schema::Bool(true) => true.into(), Schema::Bool(true) => true.into(),
Schema::Bool(false) => <()>::make_schema(gen), Schema::Bool(false) => <()>::make_schema(gen),
schema => SchemaObject { schema => {
any_of: Some(vec![schema, <()>::make_schema(gen)]), if make_any_of {
..Default::default() 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 { impl MakeSchema for serde_json::Value {
no_ref_schema!(); no_ref_schema!();
fn make_schema(_: &mut SchemaGenerator) -> Schema { fn make_schema(gen: &mut SchemaGenerator) -> Schema {
true.into() gen.schema_for_any()
} }
} }