2019-10-27 20:44:23 +00:00
|
|
|
use heck::SnakeCase;
|
2019-10-05 00:21:41 +02:00
|
|
|
use proc_macro::TokenStream;
|
|
|
|
use proc_macro2::{Ident, TokenStream as TokenStream2};
|
|
|
|
use quote::{format_ident, quote};
|
|
|
|
use syn::{
|
|
|
|
FnArg,
|
|
|
|
ItemFn,
|
|
|
|
ReturnType,
|
2020-01-14 23:16:31 +01:00
|
|
|
Type,
|
|
|
|
TypePath,
|
2019-10-05 00:21:41 +02:00
|
|
|
parse_macro_input
|
|
|
|
};
|
2019-10-06 15:03:30 +02:00
|
|
|
use std::str::FromStr;
|
2019-10-05 00:21:41 +02:00
|
|
|
|
|
|
|
pub enum Method
|
|
|
|
{
|
|
|
|
ReadAll,
|
|
|
|
Read,
|
2019-10-13 17:43:42 +02:00
|
|
|
Search,
|
2019-10-05 00:21:41 +02:00
|
|
|
Create,
|
|
|
|
UpdateAll,
|
|
|
|
Update,
|
|
|
|
DeleteAll,
|
|
|
|
Delete
|
|
|
|
}
|
|
|
|
|
2019-10-06 15:03:30 +02:00
|
|
|
impl FromStr for Method
|
|
|
|
{
|
|
|
|
type Err = String;
|
|
|
|
fn from_str(str : &str) -> Result<Self, Self::Err>
|
|
|
|
{
|
|
|
|
match str {
|
|
|
|
"ReadAll" | "read_all" => Ok(Self::ReadAll),
|
|
|
|
"Read" | "read" => Ok(Self::Read),
|
2019-10-13 17:43:42 +02:00
|
|
|
"Search" | "search" => Ok(Self::Search),
|
2019-10-06 15:03:30 +02:00
|
|
|
"Create" | "create" => Ok(Self::Create),
|
|
|
|
"UpdateAll" | "update_all" => Ok(Self::UpdateAll),
|
|
|
|
"Update" | "update" => Ok(Self::Update),
|
|
|
|
"DeleteAll" | "delete_all" => Ok(Self::DeleteAll),
|
|
|
|
"Delete" | "delete" => Ok(Self::Delete),
|
|
|
|
_ => Err("unknown method".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-05 00:21:41 +02:00
|
|
|
impl Method
|
|
|
|
{
|
2019-10-06 15:03:30 +02:00
|
|
|
pub fn trait_ident(&self) -> Ident
|
2019-10-05 00:21:41 +02:00
|
|
|
{
|
|
|
|
use Method::*;
|
|
|
|
|
|
|
|
let name = match self {
|
|
|
|
ReadAll => "ReadAll",
|
|
|
|
Read => "Read",
|
2019-10-13 17:43:42 +02:00
|
|
|
Search => "Search",
|
2019-10-05 00:21:41 +02:00
|
|
|
Create => "Create",
|
|
|
|
UpdateAll => "UpdateAll",
|
|
|
|
Update => "Update",
|
|
|
|
DeleteAll => "DeleteAll",
|
|
|
|
Delete => "Delete"
|
|
|
|
};
|
|
|
|
format_ident!("Resource{}", name)
|
|
|
|
}
|
|
|
|
|
2019-10-06 15:03:30 +02:00
|
|
|
pub fn fn_ident(&self) -> Ident
|
2019-10-05 00:21:41 +02:00
|
|
|
{
|
|
|
|
use Method::*;
|
|
|
|
|
|
|
|
let name = match self {
|
|
|
|
ReadAll => "read_all",
|
|
|
|
Read => "read",
|
2019-10-13 17:43:42 +02:00
|
|
|
Search => "search",
|
2019-10-05 00:21:41 +02:00
|
|
|
Create => "create",
|
|
|
|
UpdateAll => "update_all",
|
|
|
|
Update => "update",
|
|
|
|
DeleteAll => "delete_all",
|
|
|
|
Delete => "delete"
|
|
|
|
};
|
|
|
|
format_ident!("{}", name)
|
|
|
|
}
|
2019-10-06 15:03:30 +02:00
|
|
|
|
2019-10-27 20:44:23 +00:00
|
|
|
pub fn setup_ident(&self, resource : String) -> Ident
|
2019-10-06 15:03:30 +02:00
|
|
|
{
|
2019-10-27 20:44:23 +00:00
|
|
|
format_ident!("{}_{}_setup_impl", resource.to_snake_case(), self.fn_ident())
|
2019-10-06 15:03:30 +02:00
|
|
|
}
|
2019-10-05 00:21:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) -> TokenStream
|
|
|
|
{
|
2019-10-14 02:17:25 +02:00
|
|
|
let krate = super::krate();
|
2019-10-27 20:44:23 +00:00
|
|
|
let resource_ident = parse_macro_input!(attrs as Ident);
|
2019-10-05 00:21:41 +02:00
|
|
|
let fun = parse_macro_input!(item as ItemFn);
|
2019-10-27 20:44:23 +00:00
|
|
|
let fun_ident = &fun.sig.ident;
|
|
|
|
let fun_vis = &fun.vis;
|
2019-10-05 00:21:41 +02:00
|
|
|
|
2019-10-27 20:44:23 +00:00
|
|
|
let trait_ident = method.trait_ident();
|
|
|
|
let method_ident = method.fn_ident();
|
|
|
|
let setup_ident = method.setup_ident(resource_ident.to_string());
|
|
|
|
|
|
|
|
let (ret, is_no_content) = match &fun.sig.output {
|
2019-10-14 02:17:25 +02:00
|
|
|
ReturnType::Default => (quote!(#krate::NoContent), true),
|
2019-10-05 14:56:51 +02:00
|
|
|
ReturnType::Type(_, ty) => (quote!(#ty), false)
|
2019-10-05 00:21:41 +02:00
|
|
|
};
|
2020-01-14 23:16:31 +01:00
|
|
|
|
|
|
|
// extract arguments into pattern, ident and type
|
|
|
|
let state_ident = format_ident!("state");
|
|
|
|
let args : Vec<(TokenStream2, Ident, Type)> = fun.sig.inputs.iter().enumerate().map(|(i, arg)| match arg {
|
2019-10-05 00:21:41 +02:00
|
|
|
FnArg::Typed(arg) => {
|
|
|
|
let pat = &arg.pat;
|
2020-01-14 23:16:31 +01:00
|
|
|
let ident = if i == 0 { state_ident.clone() } else { format_ident!("arg{}", i-1) };
|
|
|
|
(quote!(#pat), ident, *arg.ty.clone())
|
2019-10-05 00:21:41 +02:00
|
|
|
},
|
|
|
|
FnArg::Receiver(_) => panic!("didn't expect self parameter")
|
|
|
|
}).collect();
|
2020-01-14 23:16:31 +01:00
|
|
|
|
|
|
|
// find the database connection if enabled and present
|
|
|
|
let repo_ident = format_ident!("database_repo");
|
2020-01-14 03:27:49 +01:00
|
|
|
let args_conn = if cfg!(feature = "database") {
|
2020-01-14 23:16:31 +01:00
|
|
|
args.iter().filter(|(pat, _, _)| pat.to_string() == "conn").nth(0)
|
2020-01-14 03:27:49 +01:00
|
|
|
} else { None };
|
2020-01-14 23:16:31 +01:00
|
|
|
let args_conn_name = args_conn.map(|(pat, _, _)| pat.to_string());
|
|
|
|
|
|
|
|
// extract the generic parameters to use
|
2020-01-14 03:27:49 +01:00
|
|
|
let mut generics : Vec<TokenStream2> = args.iter().skip(1)
|
2020-01-14 23:16:31 +01:00
|
|
|
.filter(|(pat, _, _)| Some(pat.to_string()) != args_conn_name)
|
|
|
|
.map(|(_, _, ty)| quote!(#ty)).collect();
|
2019-10-05 00:21:41 +02:00
|
|
|
generics.push(quote!(#ret));
|
2020-01-14 23:16:31 +01:00
|
|
|
|
|
|
|
// extract the definition of our method
|
2020-01-14 03:27:49 +01:00
|
|
|
let args_def : Vec<TokenStream2> = args.iter()
|
2020-01-14 23:16:31 +01:00
|
|
|
.filter(|(pat, _, _)| Some(pat.to_string()) != args_conn_name)
|
|
|
|
.map(|(_, ident, ty)| quote!(#ident : #ty)).collect();
|
|
|
|
|
|
|
|
// extract the arguments to pass over to the supplied method
|
|
|
|
let args_pass : Vec<TokenStream2> = args.iter().map(|(pat, ident, _)| if Some(pat.to_string()) != args_conn_name {
|
|
|
|
quote!(#ident)
|
|
|
|
} else {
|
|
|
|
quote!(&#ident)
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
// prepare the method block
|
2020-01-14 03:27:49 +01:00
|
|
|
let mut block = if is_no_content { quote!(#fun_ident(#(#args_pass),*); Default::default()) } else { quote!(#fun_ident(#(#args_pass),*)) };
|
2020-01-14 23:16:31 +01:00
|
|
|
if /*cfg!(feature = "database") &&*/ let Some((_, conn_ident, conn_ty)) = args_conn // https://github.com/rust-lang/rust/issues/53667
|
2020-01-14 03:27:49 +01:00
|
|
|
{
|
2020-01-14 23:16:31 +01:00
|
|
|
let conn_ty_real = match conn_ty {
|
|
|
|
Type::Reference(ty) => &*ty.elem,
|
|
|
|
ty => ty
|
|
|
|
};
|
2020-01-14 03:27:49 +01:00
|
|
|
block = quote! {
|
2020-01-14 23:16:31 +01:00
|
|
|
use #krate::export::{Future, FromState};
|
|
|
|
let #repo_ident = <#krate::export::Repo<#conn_ty_real>>::borrow_from(&#state_ident).clone();
|
|
|
|
#repo_ident.run(move |#conn_ident| {
|
2020-01-14 03:27:49 +01:00
|
|
|
#block
|
|
|
|
}).wait()
|
|
|
|
};
|
|
|
|
}
|
2019-10-05 00:21:41 +02:00
|
|
|
|
|
|
|
let output = quote! {
|
2019-10-27 20:44:23 +00:00
|
|
|
#fun
|
|
|
|
|
|
|
|
impl #krate::#trait_ident<#(#generics),*> for #resource_ident
|
|
|
|
where #resource_ident : #krate::Resource
|
2019-10-05 00:21:41 +02:00
|
|
|
{
|
2019-10-27 20:44:23 +00:00
|
|
|
fn #method_ident(#(#args_def),*) -> #ret
|
2019-10-05 14:56:51 +02:00
|
|
|
{
|
2019-10-27 20:44:23 +00:00
|
|
|
#block
|
2019-10-05 14:56:51 +02:00
|
|
|
}
|
2019-10-05 00:21:41 +02:00
|
|
|
}
|
2019-10-06 15:03:30 +02:00
|
|
|
|
|
|
|
#[deny(dead_code)]
|
2019-10-27 20:44:23 +00:00
|
|
|
#fun_vis fn #setup_ident<D : #krate::DrawResourceRoutes>(route : &mut D)
|
2019-10-06 15:03:30 +02:00
|
|
|
{
|
2019-10-27 20:44:23 +00:00
|
|
|
route.#method_ident::<#resource_ident, #(#generics),*>();
|
2019-10-06 15:03:30 +02:00
|
|
|
}
|
2019-10-05 00:21:41 +02:00
|
|
|
};
|
2020-01-14 23:16:31 +01:00
|
|
|
println!("{}", output);
|
2019-10-05 00:21:41 +02:00
|
|
|
output.into()
|
|
|
|
}
|