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
#![deny(missing_docs)]
extern crate proc_macro;
use darling::FromDeriveInput;
use heck::MixedCase;
use proc_macro::TokenStream;
use proc_macro2;
use quote::{format_ident, quote, ToTokens};
use syn::{Generics, Ident};
#[derive(FromDeriveInput)]
#[darling(attributes(pandora_request))]
struct PandoraRequest {
ident: Ident,
generics: Generics,
#[darling(default = "std::option::Option::default")]
response_type: Option<String>,
#[darling(default = "std::option::Option::default")]
error_type: Option<String>,
#[darling(default = "std::option::Option::default")]
method_name: Option<String>,
#[darling(default = "std::option::Option::default")]
encrypted: Option<bool>,
}
impl ToTokens for PandoraRequest {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let PandoraRequest {
ref ident,
ref generics,
ref response_type,
ref error_type,
ref method_name,
ref encrypted,
} = *self;
let final_response_type = format_ident!(
"{}",
response_type
.as_ref()
.map(|s| s.to_string())
.unwrap_or_else(|| format!("{}Response", ident))
);
let final_error_type = format_ident!(
"{}",
error_type
.as_ref()
.map(|s| s.to_string())
.unwrap_or_else(|| "Error".to_string())
);
let get_method_decl = if let Some(method_name) = method_name {
quote! {
fn get_method(&self) -> String {
stringify!(#method_name).to_string()
}
}
} else {
let mixed_case_method = ident.to_string().to_mixed_case();
quote! {
fn get_method(&self) -> String {
let module_name = std::module_path!();
let class_name = module_name.rsplitn(2, "::").next().expect("Could not infer a valid method name since there is no current module. Must pass #[pandora_request(method_name = \"<value>\")] as part of the derive.");
format!("{}.{}", class_name, #mixed_case_method)
}
}
};
let encrypt_expr = encrypted
.map(|b| {
quote! {
fn encrypt_request(&self) -> bool {
#b
}
}
})
.unwrap_or_else(|| quote! {});
let (imp, ty, wher) = generics.split_for_impl();
tokens.extend(quote! {
impl #imp PandoraApiRequest for #ident #ty #wher {
type Response = #final_response_type;
type Error = #final_error_type;
#encrypt_expr
#get_method_decl
}
});
}
}
#[proc_macro_derive(PandoraRequest, attributes(pandora_request))]
pub fn derive_pandora_request(input: TokenStream) -> TokenStream {
let request = PandoraRequest::from_derive_input(&syn::parse(input).unwrap())
.expect("Failed parsing macro input");
let pm2_tokens: proc_macro2::TokenStream = quote! {#request};
pm2_tokens.into()
}