Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for complex enums using OaSchema derive macro #8

Merged
merged 15 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Cargo.lock
.cargo
/target
/target
.vscode/
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ pub struct User {
impl OaSchema for User {
fn schema() -> Option<Schema> {
let mut schema = Schema::new_object();
schema.add_property("id", Schema::new_integer());
schema.add_property("name", Schema::new_string());
schema.add_property("id", ReferenceOr::Item(Schema::new_integer()));
schema.add_property("name", ReferenceOr::Item(Schema::new_string()));
Some(schema)
}
}
Expand Down
8 changes: 6 additions & 2 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ phonenumber = { optional = true, version = "0.3.2" }
sqlx = { version = "0.7", optional = true }
sqlx-core = { version = "0.7", optional = true }
tower-cookies = { version = "0.9.0", optional = true }
sid = { version = "0.4.0", optional = true, package = "sid2" }
sid2 = { version = "0.4.0", optional = true }
serde_qs = { version = "0.12", optional = true }

[features]
actix = ["actix-web"]
actix = ["actix-web", "serde_qs?/actix4"]
json = ["sqlx-core/json", "sqlx-core"]
cookies = ["tower-cookies"]
qs = ["serde_qs"]
sid = ["sid2"]
axum = ["dep:axum", "serde_qs?/axum"]

[dev-dependencies]
assert_matches = "1.5.0"
51 changes: 40 additions & 11 deletions core/src/schema.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
use std::collections::HashMap;

use oa::AdditionalProperties;
use openapiv3 as oa;
use openapiv3::{ReferenceOr, Schema};
use openapiv3::{ObjectType, ReferenceOr, Schema, SchemaData, SchemaKind, Type};

#[cfg(feature = "actix")]
mod actix;

#[cfg(feature = "axum")]
mod axum;

#[cfg(feature = "sqlx")]
mod sqlx;
#[cfg(feature = "time")]
mod time;
#[cfg(feature = "chrono")]
mod chrono;
#[cfg(feature = "cookies")]
mod cookies;
#[cfg(feature = "phonenumber")]
mod phonenumber;
#[cfg(feature = "sqlx")]
mod sqlx;
#[cfg(feature = "time")]
mod time;

mod http;
#[cfg(feature = "sid")]
Expand Down Expand Up @@ -80,8 +83,7 @@ macro_rules! impl_oa_schema_passthrough {
#[macro_export]
macro_rules! impl_oa_schema_none {
($t:ty) => {
impl $crate::OaSchema for $t {
}
impl $crate::OaSchema for $t {}
};
}

Expand Down Expand Up @@ -121,7 +123,6 @@ impl<T> OaSchema for Vec<T> where T: OaSchema {
}
}


impl<T> OaSchema for Option<T> where T: OaSchema {
fn schema_ref() -> Option<ReferenceOr<Schema>> {
let mut schema = T::schema_ref();
Expand All @@ -144,8 +145,8 @@ impl<T> OaSchema for Option<T> where T: OaSchema {
}

impl<T, E> OaSchema for Result<T, E>
where
T: OaSchema,
where
T: OaSchema,
{
fn schema_ref() -> Option<ReferenceOr<Schema>> {
T::schema_ref()
Expand All @@ -156,7 +157,35 @@ impl<T, E> OaSchema for Result<T, E>
}
}

impl<K, V> OaSchema for HashMap<K, V>
where
V: OaSchema,
{
fn schema_ref() -> Option<ReferenceOr<Schema>> {
Some(ReferenceOr::Item(Schema {
schema_data: SchemaData::default(),
schema_kind: SchemaKind::Type(Type::Object(ObjectType {
additional_properties: Some(AdditionalProperties::Schema(Box::new(
V::schema_ref()?
))),
..ObjectType::default()
})),
}))
}

fn schema() -> Option<Schema> {
Some(Schema {
schema_data: SchemaData::default(),
schema_kind: SchemaKind::Type(Type::Object(ObjectType {
additional_properties: V::schema()
.map(|s| AdditionalProperties::Schema(Box::new(ReferenceOr::Item(s)))),
..ObjectType::default()
})),
})
}
}

#[cfg(feature = "uuid")]
impl_oa_schema!(uuid::Uuid, Schema::new_string().with_format("uuid"));

impl_oa_schema!(serde_json::Value, Schema::new_object());
impl_oa_schema!(serde_json::Value, Schema::new_object());
27 changes: 10 additions & 17 deletions core/src/schema/actix.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use openapiv3 as oa;
use openapiv3::ReferenceOr;

use crate::{impl_oa_schema_none, OaSchema};

impl<T: OaSchema> OaSchema for actix_web::web::Json<T> {
Expand Down Expand Up @@ -50,21 +49,15 @@ construct_path!(A1, A2, A3);

impl<T: OaSchema> OaSchema for actix_web::web::Query<T> {
fn parameters() -> Option<Vec<ReferenceOr<oa::Parameter>>> {
Some(vec![ReferenceOr::Item(oa::Parameter::Query {
parameter_data: oa::ParameterData {
name: "query".to_string(),
description: None,
required: false,
deprecated: None,
format: oa::ParameterSchemaOrContent::Schema(T::schema_ref().unwrap()),
example: None,
examples: Default::default(),
explode: None,
extensions: Default::default(),
},
allow_reserved: false,
style: oa::QueryStyle::Form,
allow_empty_value: None,
})])
let p = oa::Parameter::query("query", T::schema_ref().unwrap());
Some(vec![ReferenceOr::Item(p)])
}
}

#[cfg(feature = "qs")]
impl<T: OaSchema> OaSchema for serde_qs::actix::QsQuery<T> {
fn parameters() -> Option<Vec<ReferenceOr<oa::Parameter>>> {
let p = oa::Parameter::query("query", T::schema_ref().unwrap());
Some(vec![ReferenceOr::Item(p)])
}
}
41 changes: 11 additions & 30 deletions core/src/schema/axum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,8 @@ impl OaSchema for axum::http::HeaderMap {}

impl<T: OaSchema> OaSchema for axum::extract::Query<T> {
fn parameters() -> Option<Vec<ReferenceOr<oa::Parameter>>> {
Some(vec![ReferenceOr::Item(oa::Parameter::Query {
parameter_data: oa::ParameterData {
name: "query".to_string(),
description: None,
required: false,
deprecated: None,
format: oa::ParameterSchemaOrContent::Schema(T::schema_ref().unwrap()),
example: None,
examples: Default::default(),
explode: None,
extensions: Default::default(),
},
allow_reserved: false,
style: oa::QueryStyle::Form,
allow_empty_value: None,
})])
let p = oa::Parameter::query("query", T::schema_ref().unwrap());
Some(vec![ReferenceOr::Item(p)])
}
}

Expand All @@ -51,20 +37,7 @@ macro_rules! construct_path {
fn parameters() -> Option<Vec<ReferenceOr<oa::Parameter>>> {
Some(vec![
$(
ReferenceOr::Item(oa::Parameter::Path {
parameter_data: oa::ParameterData {
name: stringify!($arg).to_string(),
description: None,
required: true,
deprecated: None,
format: oa::ParameterSchemaOrContent::Schema($arg::schema_ref().unwrap()),
example: None,
examples: Default::default(),
explode: None,
extensions: Default::default(),
},
style: oa::PathStyle::Simple,
})
ReferenceOr::Item(oa::Parameter::path(stringify!($arg), $arg::schema_ref().unwrap()))
),+
])
}
Expand All @@ -77,3 +50,11 @@ construct_path!(A1, A2);
construct_path!(A1, A2, A3);

impl OaSchema for axum::http::request::Parts {}

#[cfg(feature = "qs")]
impl<T: OaSchema> OaSchema for serde_qs::axum::QsQuery<T> {
fn parameters() -> Option<Vec<ReferenceOr<oa::Parameter>>> {
let p = oa::Parameter::query("query", T::schema_ref().unwrap());
Some(vec![ReferenceOr::Item(p)])
}
}
1 change: 1 addition & 0 deletions core/src/schema/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ use crate::impl_oa_schema;

impl_oa_schema!(::chrono::NaiveDate, crate::Schema::new_string().with_format("date"));
impl_oa_schema!(::chrono::DateTime<::chrono::Utc>, crate::Schema::new_string().with_format("date-time"));
impl_oa_schema!(::chrono::DateTime<::chrono::FixedOffset>, crate::Schema::new_string().with_format("date-time"));
impl_oa_schema!(::chrono::NaiveDateTime, crate::Schema::new_string().with_format("date-time"));
13 changes: 7 additions & 6 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use serde_derive_internals::{
ast::{Container, Data, Style},
Ctxt, Derive,
};
use syn::*;
use util::{derive_oaschema_enum, derive_oaschema_newtype, derive_oaschema_struct};
use syn::{PathArguments, GenericArgument, TypePath, Type, ReturnType, FnArg, parse_macro_input, DeriveInput};
use util::{derive_oaschema_enum, derive_oaschema_struct};
use crate::attr::{get_docstring, OperationAttributes};
use crate::util::derive_oaschema_newtype;

mod util;
mod attr;
Expand All @@ -31,13 +32,13 @@ pub fn derive_oaschema(item: TokenStream) -> TokenStream {
derive_oaschema_struct(id, fields, docstring)
}
Data::Struct(Style::Newtype, fields) => {
derive_oaschema_newtype(id, fields.first().unwrap(), docstring)
derive_oaschema_newtype(id, fields.first().unwrap())
}
Data::Enum(variants) => {
derive_oaschema_enum(id, variants, docstring)
derive_oaschema_enum(id, variants, &cont.attrs.tag(), docstring)
}
Data::Struct(_, _) => {
panic!("#[derive(OaSchema)] can only be used on structs with named fields or newtypes")
Data::Struct(Style::Tuple | Style::Unit, _) => {
panic!("#[derive(OaSchema)] can not be used on tuple structs")
}
}
}
Expand Down
Loading
Loading