1extern crate proc_macro;
2use core::panic;
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, DeriveInput, Ident};
7
8#[proc_macro_attribute]
10pub fn rest_model(args: TokenStream, item: TokenStream) -> TokenStream {
11 let input = parse_macro_input!(item as DeriveInput);
13 let original = input.clone();
14 let struct_name = input.ident;
15
16 let mut get = false;
17 let mut get_with_id = false;
18 let mut put = false;
19 let mut patch = false;
20 let mut delete = false;
21
22 let mut db: Option<Ident> = None;
24 let mut db_name: Option<Ident> = None;
25 let mut table_name: Option<Ident> = None;
26 let parser = syn::meta::parser(|meta| {
27 if meta.path.is_ident("db") {
28 let mut i = 0;
29 meta.parse_nested_meta(|meta| {
30 let ident = meta.path.get_ident().unwrap();
31 if i == 0 {
32 db = Some(ident.clone());
33 } else if i == 1 {
34 db_name = Some(ident.clone());
35 } else if i == 2 {
36 table_name = Some(ident.clone());
37 } else {
38 return Err(meta.error("rest_model db only support 3 params"));
39 }
40 i += 1;
41 Ok(())
42 })?;
43 if i != 1 && i != 3 {
44 return Err(meta.error("rest_model db only support 1 or 3 params"));
45 }
46 Ok(())
47 } else if meta.path.is_ident("with") {
48 meta.parse_nested_meta(|meta| {
49 if meta.path.is_ident("get") {
50 get = true;
51 Ok(())
52 } else if meta.path.is_ident("get_with_id") {
53 get_with_id = true;
54 Ok(())
55 } else if meta.path.is_ident("put") {
56 put = true;
57 Ok(())
58 } else if meta.path.is_ident("patch") {
59 patch = true;
60 Ok(())
61 } else if meta.path.is_ident("delete") {
62 delete = true;
63 Ok(())
64 } else if meta.path.is_ident("all") {
65 get = true;
66 get_with_id = true;
67 put = true;
68 patch = true;
69 delete = true;
70 Ok(())
71 } else {
72 Err(meta.error("unsupported rest_model with property"))
73 }
74 })
75 } else {
76 Err(meta.error(format!(
77 "unsupported rest_model property `{}`",
78 meta.path.get_ident().unwrap().to_string()
79 )))
80 }
81 });
82
83 parse_macro_input!(args with parser);
84
85 if db.is_none() {
86 panic!("Db must be specified");
87 }
88
89 let mut methods = quote! {
91 impl rest_model::method::Init<#struct_name, #db> for #struct_name {}
92 };
93
94 if db_name.is_some() && table_name.is_some() {
95 methods.extend(quote! {
96 impl rest_model::RestModel for #struct_name {
97 fn get_db_name() -> &'static str {
98 #db_name
99 }
100 fn get_table_name() -> &'static str {
101 #table_name
102 }
103 }
104 });
105 }
106
107 if get_with_id {
108 methods.extend(quote! {
109 impl rest_model::method::GetWithId<#struct_name, #db> for #struct_name {}
110 });
111 }
112 if get {
113 methods.extend(quote! {
114 impl rest_model::method::Get<#struct_name, #db> for #struct_name {}
115 });
116 }
117 if put {
118 methods.extend(quote! {
119 impl rest_model::method::Put<#struct_name, #db> for #struct_name {}
120 });
121 }
122 if patch {
123 methods.extend(quote! {
124 impl rest_model::method::Patch<#struct_name, #db> for #struct_name {}
125 });
126 }
127 if delete {
128 methods.extend(quote! {
129 impl rest_model::method::Delete<#struct_name, #db> for #struct_name {}
130 });
131 }
132
133 let expanded = quote! {
135 #original
136 #methods
137 };
138
139 TokenStream::from(expanded)
141}