From d1c7ac5887f4c8e7336feb4fd418ae43cfec3ebd Mon Sep 17 00:00:00 2001 From: Dominic Date: Tue, 1 Oct 2019 15:33:05 +0200 Subject: [PATCH] introduce OpenapiSchema type --- src/helper.rs | 35 +++++--------- src/lib.rs | 2 +- src/openapi/router.rs | 26 ++-------- src/openapi/types.rs | 107 +++++++++++++++++++++++++----------------- src/result.rs | 30 ++---------- 5 files changed, 85 insertions(+), 115 deletions(-) diff --git a/src/helper.rs b/src/helper.rs index e34515e..4b2789e 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -29,14 +29,9 @@ macro_rules! rest_struct { impl ::gotham_restful::OpenapiType for $struct_name { - fn schema_name() -> Option + fn to_schema() -> ::gotham_restful::OpenapiSchema { - Some(stringify!($struct_name).to_string()) - } - - fn to_schema() -> ::gotham_restful::helper::openapi::SchemaKind - { - use ::gotham_restful::helper::openapi::*; + use ::gotham_restful::{helper::openapi::*, OpenapiSchema}; let mut properties : IndexMap>> = IndexMap::new(); let mut required : Vec = Vec::new(); @@ -44,31 +39,23 @@ macro_rules! rest_struct { $( properties.insert( stringify!($field_id).to_string(), - ReferenceOr::Item(Box::new(Schema { - schema_data: SchemaData { - nullable: false, - read_only: false, - write_only: false, - deprecated: false, - external_docs: None, - example: None, - title: <$field_ty>::schema_name(), - description: None, - discriminator: None, - default: None - }, - schema_kind: <$field_ty>::to_schema() - })) + ReferenceOr::Item(Box::new(<$field_ty>::to_schema().to_schema())) ); )* - SchemaKind::Type(Type::Object(ObjectType { + let schema = SchemaKind::Type(Type::Object(ObjectType { properties, required, additional_properties: None, min_properties: None, max_properties: None - })) + })); + + OpenapiSchema { + name: Some(stringify!($struct_name).to_string()), + nullable: false, + schema + } } } } diff --git a/src/lib.rs b/src/lib.rs index 715cb90..470d119 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ pub mod openapi; #[cfg(feature = "openapi")] pub use openapi::{ router::{GetOpenapi, OpenapiRouter}, - types::OpenapiType + types::{OpenapiSchema, OpenapiType} }; mod resource; diff --git a/src/openapi/router.rs b/src/openapi/router.rs index 1539f4d..1bade2e 100644 --- a/src/openapi/router.rs +++ b/src/openapi/router.rs @@ -19,7 +19,7 @@ use mime::{APPLICATION_JSON, TEXT_PLAIN}; use openapiv3::{ Components, MediaType, OpenAPI, Operation, Parameter, ParameterData, ParameterSchemaOrContent, PathItem, PathStyle, Paths, ReferenceOr, ReferenceOr::Item, ReferenceOr::Reference, RequestBody, Response, Responses, - Schema, SchemaData, Server, StatusCode + Server, StatusCode }; use serde::de::DeserializeOwned; use std::panic::RefUnwindSafe; @@ -71,22 +71,9 @@ impl OpenapiRouter fn add_schema(&mut self, path : &str, method : &str, desc : &str) -> String { - let name = T::schema_name().unwrap_or_else(|| format!("path_{}_{}_{}", path, method, desc)); - let item = Schema { - schema_data: SchemaData { - nullable: false, - read_only: false, - write_only: false, - deprecated: false, - external_docs: None, - example: None, - title: Some(name.to_string()), - description: None, - discriminator: None, - default: None - }, - schema_kind: T::to_schema() - }; + let schema = T::to_schema(); + let name = schema.name.clone().unwrap_or_else(|| format!("path_{}_{}_{}", path, method, desc)); + let item = schema.to_schema(); match &mut self.0.components { Some(comp) => { comp.schemas.insert(name.to_string(), Item(item)); @@ -181,10 +168,7 @@ fn new_operation(schema : &str, path_params : Vec<&str>, body_schema : Option<&s description: None, required: true, deprecated: None, - format: ParameterSchemaOrContent::Schema(Item(Schema { - schema_data: SchemaData::default(), - schema_kind: String::to_schema() - })), + format: ParameterSchemaOrContent::Schema(Item(String::to_schema().to_schema())), example: None, examples: IndexMap::new() }, diff --git a/src/openapi/types.rs b/src/openapi/types.rs index db26d67..cce241d 100644 --- a/src/openapi/types.rs +++ b/src/openapi/types.rs @@ -3,34 +3,68 @@ use chrono::{ Date, DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, Utc }; use openapiv3::{ - ArrayType, IntegerType, NumberType, ObjectType, ReferenceOr::Item, Schema, SchemaData, SchemaKind, - StringFormat, StringType, Type, VariantOrUnknownOrEmpty + ArrayType, IntegerType, NumberType, ObjectType, ReferenceOr::Item, ReferenceOr::Reference, Schema, + SchemaData, SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty }; +#[derive(Debug, Clone, PartialEq)] +pub struct OpenapiSchema +{ + /// The name of this schema. If it is None, the schema will be inlined. + pub name : Option, + pub nullable : bool, + pub schema : SchemaKind +} + +impl OpenapiSchema +{ + pub fn new(schema : SchemaKind) -> Self + { + Self { + name: None, + nullable: false, + schema + } + } + + pub fn to_schema(self) -> Schema + { + Schema { + schema_data: SchemaData { + nullable: self.nullable, + read_only: false, + write_only: false, + deprecated: false, + external_docs: None, + example: None, + title: self.name, + description: None, + discriminator: None, + default: None + }, + schema_kind: self.schema + } + } +} pub trait OpenapiType { - fn schema_name() -> Option - { - None - } - - fn to_schema() -> SchemaKind; + fn to_schema() -> OpenapiSchema; } impl OpenapiType for () { - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { - SchemaKind::Type(Type::Object(ObjectType::default())) + OpenapiSchema::new(SchemaKind::Type(Type::Object(ObjectType::default()))) } } impl OpenapiType for bool { - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { - SchemaKind::Type(Type::Boolean{}) + OpenapiSchema::new(SchemaKind::Type(Type::Boolean{})) } } @@ -38,9 +72,9 @@ macro_rules! int_types { ($($int_ty:ty),*) => {$( impl OpenapiType for $int_ty { - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { - SchemaKind::Type(Type::Integer(IntegerType::default())) + OpenapiSchema::new(SchemaKind::Type(Type::Integer(IntegerType::default()))) } } )*} @@ -52,9 +86,9 @@ macro_rules! num_types { ($($num_ty:ty),*) => {$( impl OpenapiType for $num_ty { - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { - SchemaKind::Type(Type::Number(NumberType::default())) + OpenapiSchema::new(SchemaKind::Type(Type::Number(NumberType::default()))) } } )*} @@ -66,9 +100,9 @@ macro_rules! str_types { ($($str_ty:ty),*) => {$( impl OpenapiType for $str_ty { - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { - SchemaKind::Type(Type::String(StringType::default())) + OpenapiSchema::new(SchemaKind::Type(Type::String(StringType::default()))) } } )*}; @@ -76,13 +110,13 @@ macro_rules! str_types { (format = $format:ident, $($str_ty:ty),*) => {$( impl OpenapiType for $str_ty { - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { - SchemaKind::Type(Type::String(StringType { + OpenapiSchema::new(SchemaKind::Type(Type::String(StringType { format: VariantOrUnknownOrEmpty::Item(StringFormat::$format), pattern: None, enumeration: Vec::new() - })) + }))) } } )*}; @@ -92,33 +126,18 @@ str_types!(String, &str); impl OpenapiType for Vec { - fn schema_name() -> Option + fn to_schema() -> OpenapiSchema { - T::schema_name().map(|name| format!("{}Array", name)) - } - - fn to_schema() -> SchemaKind - { - SchemaKind::Type(Type::Array(ArrayType { - items: Item(Box::new(Schema { - schema_data: SchemaData { - nullable: false, - read_only: false, - write_only: false, - deprecated: false, - external_docs: None, - example: None, - title: T::schema_name(), - description: None, - discriminator: None, - default: None - }, - schema_kind: T::to_schema() - })), + let schema = T::to_schema(); + OpenapiSchema::new(SchemaKind::Type(Type::Array(ArrayType { + items: match schema.name { + Some(name) => Reference { reference: format!("#/components/schemas/{}", name) }, + None => Item(Box::new(schema.to_schema())) + }, min_items: None, max_items: None, unique_items: false - })) + }))) } } diff --git a/src/result.rs b/src/result.rs index 3f8eed1..b73bee5 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,6 +1,6 @@ use crate::{ResourceType, StatusCode}; #[cfg(feature = "openapi")] -use openapiv3::SchemaKind; +use crate::OpenapiSchema; use serde::Serialize; use serde_json::error::Error as SerdeJsonError; use std::error::Error; @@ -11,21 +11,13 @@ pub trait ResourceResult fn to_json(&self) -> Result<(StatusCode, String), SerdeJsonError>; #[cfg(feature = "openapi")] - fn schema_name() -> Option; - - #[cfg(feature = "openapi")] - fn to_schema() -> SchemaKind; + fn to_schema() -> OpenapiSchema; } #[cfg(feature = "openapi")] impl crate::OpenapiType for Res { - fn schema_name() -> Option - { - Self::schema_name() - } - - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { Self::to_schema() } @@ -64,13 +56,7 @@ impl ResourceResult for Result } #[cfg(feature = "openapi")] - fn schema_name() -> Option - { - R::schema_name() - } - - #[cfg(feature = "openapi")] - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { R::to_schema() } @@ -95,13 +81,7 @@ impl ResourceResult for Success } #[cfg(feature = "openapi")] - fn schema_name() -> Option - { - T::schema_name() - } - - #[cfg(feature = "openapi")] - fn to_schema() -> SchemaKind + fn to_schema() -> OpenapiSchema { T::to_schema() }