1
0
Fork 0
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:
Dominic 2019-10-02 10:59:25 +02:00
parent a4185a5665
commit 4ef216e8c8
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
17 changed files with 273 additions and 47 deletions

View 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 = []

View 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)
}

View 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()
}