#![allow(non_snake_case)]
use proc_macro::TokenStream;
use quote::quote;
use serde_derive_internals::{
ast::{Container, Data, Style},
Ctxt, Derive,
};
use syn::*;
use util::{derive_oaschema_enum, derive_oaschema_newtype, derive_oaschema_struct};
mod util;
#[proc_macro_derive(OaSchema, attributes(openapi))]
pub fn derive_oaschema(item: TokenStream) -> TokenStream {
let ast = parse_macro_input!(item as DeriveInput);
let cont = {
let ctxt = Ctxt::new();
let cont = Container::from_ast(&ctxt, &ast, Derive::Deserialize);
ctxt.check().unwrap();
cont.unwrap()
};
let id = &cont.ident;
match &cont.data {
Data::Struct(Style::Struct, fields) => derive_oaschema_struct(id, fields),
Data::Struct(Style::Newtype, fields) => {
derive_oaschema_newtype(id, fields.first().unwrap())
}
Data::Struct(_, _) => {
panic!("#[ormlite] can only be used on structs with named fields or newtypes")
}
Data::Enum(variants) => derive_oaschema_enum(id, variants),
}
}
#[proc_macro_attribute]
pub fn openapi(_args: TokenStream, input: TokenStream) -> TokenStream {
let span = proc_macro2::Span::call_site();
let mut ast = parse_macro_input!(input as syn::ItemFn);
let name = &ast.sig.ident;
let marker_struct_name = syn::Ident::new(&format!("__{}__metadata", name), name.span());
ast.sig.asyncness = None;
let output_type = match std::mem::replace(&mut ast.sig.output, ReturnType::Default) {
ReturnType::Type(_, ty) => ty,
ReturnType::Default => Box::new(syn::parse2(quote!(())).unwrap()),
};
ast.sig.output = ReturnType::Type(
Token,
Box::new(syn::parse2(quote!(::oasgen::TypedResponseFuture<impl std::future::Future<Output=#output_type>, #marker_struct_name>)).expect("parsing empty type")),
);
let block = &ast.block;
ast.block = Box::new(
syn::parse2(quote!({
::oasgen::TypedResponseFuture::new(async move #block)
}))
.expect("parsing empty block"),
);
let public = ast.vis.clone();
let bounds = ast.sig.inputs.iter().map(|input| {
let ty = match input {
syn::FnArg::Receiver(_) => panic!("receiver not supported"),
syn::FnArg::Typed(ty) => &ty.ty,
};
quote! { #ty: ::oasgen::OaSchema }
});
let marker_struct_impl_FunctionMetadata = quote! {
impl ::oasgen::FunctionMetadata for #marker_struct_name where
#output_type: OaSchema
#(, #bounds )*
{
fn operation_id() -> Option<&'static str> {
None
}
fn summary() -> Option<&'static str> {
None
}
fn description() -> Option<&'static str> {
None
}
}
};
let expanded = quote! {
#ast
#[allow(non_camel_case_types)]
#public struct #marker_struct_name;
#marker_struct_impl_FunctionMetadata
};
TokenStream::from(expanded)
}