1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use heck::SnakeCase;
use proc_macro::TokenStream;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::{format_ident, quote};
use syn::{
FnArg,
ItemFn,
ReturnType,
parse_macro_input
};
use std::str::FromStr;
pub enum Method
{
ReadAll,
Read,
Search,
Create,
UpdateAll,
Update,
DeleteAll,
Delete
}
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),
"Search" | "search" => Ok(Self::Search),
"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())
}
}
}
impl Method
{
pub fn trait_ident(&self) -> Ident
{
use Method::*;
let name = match self {
ReadAll => "ReadAll",
Read => "Read",
Search => "Search",
Create => "Create",
UpdateAll => "UpdateAll",
Update => "Update",
DeleteAll => "DeleteAll",
Delete => "Delete"
};
format_ident!("Resource{}", name)
}
pub fn fn_ident(&self) -> Ident
{
use Method::*;
let name = match self {
ReadAll => "read_all",
Read => "read",
Search => "search",
Create => "create",
UpdateAll => "update_all",
Update => "update",
DeleteAll => "delete_all",
Delete => "delete"
};
format_ident!("{}", name)
}
pub fn setup_ident(&self, resource : String) -> Ident
{
format_ident!("{}_{}_setup_impl", resource.to_snake_case(), self.fn_ident())
}
}
pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) -> TokenStream
{
let krate = super::krate();
let resource_ident = parse_macro_input!(attrs as Ident);
let fun = parse_macro_input!(item as ItemFn);
let fun_ident = &fun.sig.ident;
let fun_vis = &fun.vis;
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 {
ReturnType::Default => (quote!(#krate::NoContent), true),
ReturnType::Type(_, ty) => (quote!(#ty), false)
};
let args : Vec<(TokenStream2, TokenStream2)> = fun.sig.inputs.iter().map(|arg| match arg {
FnArg::Typed(arg) => {
let pat = &arg.pat;
let ty = &arg.ty;
(quote!(#pat), quote!(#ty))
},
FnArg::Receiver(_) => panic!("didn't expect self parameter")
}).collect();
let mut generics : Vec<TokenStream2> = args.iter().skip(1).map(|(_, ty)| quote!(#ty)).collect();
generics.push(quote!(#ret));
let args_def : Vec<TokenStream2> = args.iter().map(|(pat, ty)| quote!(#pat : #ty)).collect();
let args_pass : Vec<TokenStream2> = args.iter().map(|(pat, _)| quote!(#pat)).collect();
let block = if is_no_content { quote!(#fun_ident(#(#args_pass),*); Default::default()) } else { quote!(#fun_ident(#(#args_pass),*)) };
let output = quote! {
#fun
impl #krate::#trait_ident<#(#generics),*> for #resource_ident
where #resource_ident : #krate::Resource
{
fn #method_ident(#(#args_def),*) -> #ret
{
#block
}
}
#[deny(dead_code)]
#fun_vis fn #setup_ident<D : #krate::DrawResourceRoutes>(route : &mut D)
{
route.#method_ident::<#resource_ident, #(#generics),*>();
}
};
output.into()
}