mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-02-22 20:52:27 +00:00
add proc macro derive for openapitype
This commit is contained in:
parent
a4185a5665
commit
4ef216e8c8
17 changed files with 273 additions and 47 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
@ -213,6 +213,19 @@ dependencies = [
|
|||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"fake 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gotham 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gotham_restful 0.0.1",
|
||||
"gotham_restful_derive 0.0.1",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.5"
|
||||
|
@ -328,19 +341,26 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "gotham-restful"
|
||||
name = "gotham_derive"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gotham_restful"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fake 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gotham 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gotham_derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openapiv3 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -348,12 +368,15 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "gotham_derive"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "gotham_restful_derive"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fake 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
43
Cargo.toml
43
Cargo.toml
|
@ -1,39 +1,8 @@
|
|||
# -*- eval: (cargo-minor-mode 1) -*-
|
||||
|
||||
[package]
|
||||
name = "gotham-restful"
|
||||
version = "0.0.1"
|
||||
authors = ["Dominic Meiser <git@msrd0.de>"]
|
||||
edition = "2018"
|
||||
description = "RESTful additions for Gotham"
|
||||
keywords = ["gotham", "rest", "restful"]
|
||||
license = "EPL-2.0"
|
||||
readme = "README.md"
|
||||
include = ["src/**/*", "Cargo.toml", "LICENSE"]
|
||||
repository = "https://gitlab.com/msrd0/gotham-restful"
|
||||
|
||||
[badges]
|
||||
gitlab = { repository = "msrd0/gotham-restful", branch = "master" }
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", optional = true }
|
||||
failure = "0.1"
|
||||
futures = "0.1"
|
||||
gotham = "0.4"
|
||||
gotham_derive = "0.4"
|
||||
hyper = "0.12"
|
||||
indexmap = { version = "1.0", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
mime = "0.3"
|
||||
openapiv3 = { version = "0.3", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
fake = "2.2"
|
||||
log = "0.4"
|
||||
log4rs = { version = "0.8", features = ["console_appender"], default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["openapi", "chrono"]
|
||||
openapi = ["indexmap", "log", "openapiv3"]
|
||||
[workspace]
|
||||
members = [
|
||||
"gotham_restful",
|
||||
"gotham_restful_derive",
|
||||
"example"
|
||||
]
|
||||
|
|
28
example/Cargo.toml
Normal file
28
example/Cargo.toml
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- eval: (cargo-minor-mode 1) -*-
|
||||
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.0.1"
|
||||
authors = ["Dominic Meiser <git@msrd0.de>"]
|
||||
edition = "2018"
|
||||
license = "Unlicense"
|
||||
readme = "README.md"
|
||||
include = ["src/**/*", "Cargo.toml", "LICENSE"]
|
||||
repository = "https://gitlab.com/msrd0/gotham-restful"
|
||||
|
||||
[badges]
|
||||
gitlab = { repository = "msrd0/gotham-restful", branch = "master" }
|
||||
|
||||
[dependencies]
|
||||
fake = "2.2"
|
||||
gotham = "0.4"
|
||||
gotham_restful = { path = "../gotham_restful", features = ["openapi"] }
|
||||
gotham_restful_derive = { path = "../gotham_restful_derive", features = ["openapi"] }
|
||||
log = "0.4"
|
||||
log4rs = { version = "0.8", features = ["console_appender"], default-features = false }
|
||||
serde = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
fake = "2.2"
|
||||
log = "0.4"
|
||||
log4rs = { version = "0.8", features = ["console_appender"], default-features = false }
|
24
example/LICENSE
Normal file
24
example/LICENSE
Normal file
|
@ -0,0 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
|
@ -1,4 +1,5 @@
|
|||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate gotham_restful_derive;
|
||||
|
||||
use fake::{faker::internet::en::Username, Fake};
|
||||
use gotham::{
|
||||
|
@ -14,6 +15,7 @@ use log4rs::{
|
|||
config::{Appender, Config, Root},
|
||||
encode::pattern::PatternEncoder
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
rest_resource!{Users, route => {
|
||||
route.read_all::<Self, _>();
|
||||
|
@ -23,9 +25,10 @@ rest_resource!{Users, route => {
|
|||
route.update::<Self, _, _, _>();
|
||||
}}
|
||||
|
||||
rest_struct!{User {
|
||||
#[derive(Deserialize, OpenapiType, Serialize)]
|
||||
struct User {
|
||||
username : String
|
||||
}}
|
||||
}
|
||||
|
||||
impl ResourceReadAll<Success<Vec<Option<User>>>> for Users
|
||||
{
|
34
gotham_restful/Cargo.toml
Normal file
34
gotham_restful/Cargo.toml
Normal file
|
@ -0,0 +1,34 @@
|
|||
# -*- eval: (cargo-minor-mode 1) -*-
|
||||
|
||||
[package]
|
||||
name = "gotham_restful"
|
||||
version = "0.0.1"
|
||||
authors = ["Dominic Meiser <git@msrd0.de>"]
|
||||
edition = "2018"
|
||||
description = "RESTful additions for Gotham"
|
||||
keywords = ["gotham", "rest", "restful"]
|
||||
license = "EPL-2.0"
|
||||
readme = "../README.md"
|
||||
include = ["src/**/*", "Cargo.toml", "../LICENSE"]
|
||||
repository = "https://gitlab.com/msrd0/gotham-restful"
|
||||
|
||||
[badges]
|
||||
gitlab = { repository = "msrd0/gotham-restful", branch = "master" }
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", optional = true }
|
||||
failure = "0.1"
|
||||
futures = "0.1"
|
||||
gotham = "0.4"
|
||||
gotham_derive = "0.4"
|
||||
hyper = "0.12"
|
||||
indexmap = { version = "1.0", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
mime = "0.3"
|
||||
openapiv3 = { version = "0.3", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
[features]
|
||||
default = ["openapi", "chrono"]
|
||||
openapi = ["indexmap", "log", "openapiv3"]
|
33
gotham_restful_derive/Cargo.toml
Normal file
33
gotham_restful_derive/Cargo.toml
Normal file
|
@ -0,0 +1,33 @@
|
|||
# -*- eval: (cargo-minor-mode 1) -*-
|
||||
|
||||
[package]
|
||||
name = "gotham_restful_derive"
|
||||
version = "0.0.1"
|
||||
authors = ["Dominic Meiser <git@msrd0.de>"]
|
||||
edition = "2018"
|
||||
description = "RESTful additions for Gotham - Derive"
|
||||
keywords = ["gotham", "rest", "restful", "derive"]
|
||||
license = "EPL-2.0"
|
||||
readme = "../README.md"
|
||||
include = ["src/**/*", "Cargo.toml", "../LICENSE"]
|
||||
repository = "https://gitlab.com/msrd0/gotham-restful"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[badges]
|
||||
gitlab = { repository = "msrd0/gotham-restful", branch = "master" }
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "1", features = ["extra-traits", "full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = "2.2"
|
||||
log = "0.4"
|
||||
log4rs = { version = "0.8", features = ["console_appender"], default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["openapi"]
|
||||
openapi = []
|
13
gotham_restful_derive/src/lib.rs
Normal file
13
gotham_restful_derive/src/lib.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[cfg(feature = "openapi")]
|
||||
mod openapi_type;
|
||||
|
||||
#[cfg(feature = "openapi")]
|
||||
#[proc_macro_derive(OpenapiType)]
|
||||
pub fn derive_openapi_type(tokens : TokenStream) -> TokenStream
|
||||
{
|
||||
openapi_type::expand(tokens)
|
||||
}
|
99
gotham_restful_derive/src/openapi_type.rs
Normal file
99
gotham_restful_derive/src/openapi_type.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
Field,
|
||||
Fields,
|
||||
ItemStruct,
|
||||
parse_macro_input
|
||||
};
|
||||
|
||||
fn expand_field(field : &Field) -> TokenStream2
|
||||
{
|
||||
let ident = match &field.ident {
|
||||
Some(ident) => ident,
|
||||
None => panic!("Fields without ident are not supported")
|
||||
};
|
||||
let ty = &field.ty;
|
||||
|
||||
quote! {{
|
||||
let mut schema = <#ty>::to_schema();
|
||||
|
||||
if schema.nullable
|
||||
{
|
||||
schema.nullable = false;
|
||||
schema.name = schema.name.map(|name|
|
||||
if name.ends_with("OrNull") { name[..(name.len()-6)].to_string() } else { name });
|
||||
}
|
||||
else
|
||||
{
|
||||
required.push(stringify!(#ident).to_string());
|
||||
}
|
||||
|
||||
match schema.name.clone() {
|
||||
Some(name) => {
|
||||
properties.insert(
|
||||
stringify!(#ident).to_string(),
|
||||
ReferenceOr::Reference { reference: format!("#/components/schemas/{}", name) }
|
||||
);
|
||||
dependencies.insert(name, schema);
|
||||
},
|
||||
None => {
|
||||
properties.insert(
|
||||
stringify!(#ident).to_string(),
|
||||
ReferenceOr::Item(Box::new(<#ty>::to_schema().to_schema()))
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
pub fn expand(tokens : proc_macro::TokenStream) -> TokenStream
|
||||
{
|
||||
let input = parse_macro_input!(tokens as ItemStruct);
|
||||
|
||||
let ident = input.ident;
|
||||
let generics = input.generics;
|
||||
|
||||
let fields : Vec<TokenStream2> = match input.fields {
|
||||
Fields::Named(fields) => {
|
||||
fields.named.iter().map(|field| expand_field(field)).collect()
|
||||
},
|
||||
Fields::Unnamed(_) => panic!("Unnamed fields are not supported"),
|
||||
Fields::Unit => Vec::new()
|
||||
};
|
||||
|
||||
let output = quote!{
|
||||
impl #generics ::gotham_restful::OpenapiType for #ident #generics
|
||||
{
|
||||
fn to_schema() -> ::gotham_restful::OpenapiSchema
|
||||
{
|
||||
use ::gotham_restful::{helper::openapi::*, OpenapiSchema};
|
||||
|
||||
let mut properties : IndexMap<String, ReferenceOr<Box<Schema>>> = IndexMap::new();
|
||||
let mut required : Vec<String> = Vec::new();
|
||||
let mut dependencies : IndexMap<String, OpenapiSchema> = IndexMap::new();
|
||||
|
||||
#(#fields)*
|
||||
|
||||
let schema = SchemaKind::Type(Type::Object(ObjectType {
|
||||
properties,
|
||||
required,
|
||||
additional_properties: None,
|
||||
min_properties: None,
|
||||
max_properties: None
|
||||
}));
|
||||
|
||||
OpenapiSchema {
|
||||
name: Some(stringify!(#ident).to_string()),
|
||||
nullable: false,
|
||||
schema,
|
||||
dependencies
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("output: {}", output);
|
||||
output.into()
|
||||
}
|
Loading…
Add table
Reference in a new issue