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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#![warn(rust_2018_idioms)]
#![doc(html_root_url = "https://docs.rs/oauth1-request-derive/0.5.0")]
#[macro_use]
mod meta;
mod container;
mod field;
mod method_body;
mod util;
use proc_macro2::{Span, TokenStream};
use proc_macro_crate::FoundCrate;
use proc_macro_error::{abort, abort_if_dirty, emit_error, proc_macro_error};
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{
parse_macro_input, parse_quote, Data, DataStruct, DeriveInput, Fields, GenericParam, Generics,
Ident,
};
use self::container::ContainerMeta;
use self::field::Field;
use self::method_body::MethodBody;
#[proc_macro_error]
#[proc_macro_derive(Request, attributes(oauth1))]
pub fn derive_oauth1_authorize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand_derive_oauth1_authorize(input).into()
}
fn expand_derive_oauth1_authorize(mut input: DeriveInput) -> TokenStream {
let name = &input.ident;
let span = input.span();
let meta = ContainerMeta::new(input.attrs);
let use_oauth1_request = if let Some(krate) = meta.krate {
quote! {
use #krate as _oauth1_request;
}
} else {
let krate;
let krate = match proc_macro_crate::crate_name("oauth1-request") {
Ok(FoundCrate::Name(k)) => {
krate = k;
&*krate
}
Ok(FoundCrate::Itself) => {
krate = std::env::var("CARGO_CRATE_NAME").unwrap();
&*krate
}
Err(proc_macro_crate::Error::CargoManifestDirNotSet) => "oauth1_request",
Err(e) => Err(e).unwrap(),
};
let krate = Ident::new(krate, Span::call_site());
quote! {
extern crate #krate as _oauth1_request;
}
};
add_trait_bounds(&mut input.generics);
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
proc_macro_error::set_dummy(quote! {
const _: () = {
#use_oauth1_request
impl #impl_generics _oauth1_request::Request for #name #ty_generics
#where_clause
{
fn serialize<S>(&self, serializer: S) -> S::Output
where
S: _oauth1_request::serializer::Serializer,
{
unimplemented!();
}
}
};
});
let fields = match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => fields,
_ => abort!(span, "expected a struct with named fields"),
};
let mut fields: Vec<_> = fields.named.into_iter().map(Field::new).collect();
fields.sort_by_cached_key(|f| f.name().string_value());
fields.iter().fold(String::new(), |prev_name, f| {
let name = f.name();
let (name, span) = (name.string_value(), name.span());
if name == prev_name {
emit_error!(span, "duplicate parameter \"{}\"", name);
}
name
});
abort_if_dirty();
let body = MethodBody::new(&fields);
quote_spanned! {Span::mixed_site()=>
const _: () = {
#use_oauth1_request
#[automatically_derived]
impl #impl_generics _oauth1_request::Request for #name #ty_generics
#where_clause
{
fn serialize<_S>(&self, mut serializer: _S) -> _S::Output
where
_S: _oauth1_request::serializer::Serializer,
{
#body
}
}
};
}
}
fn add_trait_bounds(generics: &mut Generics) {
for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param {
type_param.bounds.push(parse_quote!(::core::fmt::Display));
}
}
}