mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-02-23 04:52:28 +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)",
|
"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]]
|
[[package]]
|
||||||
name = "failure"
|
name = "failure"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -328,19 +341,26 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[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"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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 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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -348,12 +368,15 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gotham_derive"
|
name = "gotham_restful_derive"
|
||||||
version = "0.4.0"
|
version = "0.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fake 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 0.15.44 (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]]
|
[[package]]
|
||||||
|
|
43
Cargo.toml
43
Cargo.toml
|
@ -1,39 +1,8 @@
|
||||||
# -*- eval: (cargo-minor-mode 1) -*-
|
# -*- eval: (cargo-minor-mode 1) -*-
|
||||||
|
|
||||||
[package]
|
[workspace]
|
||||||
name = "gotham-restful"
|
members = [
|
||||||
version = "0.0.1"
|
"gotham_restful",
|
||||||
authors = ["Dominic Meiser <git@msrd0.de>"]
|
"gotham_restful_derive",
|
||||||
edition = "2018"
|
"example"
|
||||||
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"]
|
|
||||||
|
|
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 log;
|
||||||
|
#[macro_use] extern crate gotham_restful_derive;
|
||||||
|
|
||||||
use fake::{faker::internet::en::Username, Fake};
|
use fake::{faker::internet::en::Username, Fake};
|
||||||
use gotham::{
|
use gotham::{
|
||||||
|
@ -14,6 +15,7 @@ use log4rs::{
|
||||||
config::{Appender, Config, Root},
|
config::{Appender, Config, Root},
|
||||||
encode::pattern::PatternEncoder
|
encode::pattern::PatternEncoder
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
rest_resource!{Users, route => {
|
rest_resource!{Users, route => {
|
||||||
route.read_all::<Self, _>();
|
route.read_all::<Self, _>();
|
||||||
|
@ -23,9 +25,10 @@ rest_resource!{Users, route => {
|
||||||
route.update::<Self, _, _, _>();
|
route.update::<Self, _, _, _>();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
rest_struct!{User {
|
#[derive(Deserialize, OpenapiType, Serialize)]
|
||||||
|
struct User {
|
||||||
username : String
|
username : String
|
||||||
}}
|
}
|
||||||
|
|
||||||
impl ResourceReadAll<Success<Vec<Option<User>>>> for Users
|
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