mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-04-20 06:54:46 +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
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
Add a link
Reference in a new issue