-
Notifications
You must be signed in to change notification settings - Fork 0
/
default_args.rs
397 lines (355 loc) · 12.9 KB
/
default_args.rs
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{parse, punctuated, Token};
use crate::utils::{capitalize, inner_attributes, outer_attributes};
#[derive(Debug)]
pub struct DefaultFn {
pub attrs: Vec<syn::Attribute>,
pub vis: syn::Visibility,
pub sig: KeyWordSignature,
pub block: Box<syn::Block>,
}
impl parse::Parse for DefaultFn {
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
let mut attrs: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?;
let vis: syn::Visibility = input.parse()?;
let sig: KeyWordSignature = input.parse()?;
let content: parse::ParseBuffer;
let brace_token: syn::token::Brace = syn::braced!(content in input);
let input: parse::ParseStream = &content;
while input.peek(Token![#]) && input.peek2(Token![!]) {
attrs.push(input.call(single_parse_inner)?);
}
let stmts: Vec<syn::Stmt> = content.call(syn::Block::parse_within)?;
Ok(Self {
attrs,
vis,
sig,
block: Box::new(syn::Block { brace_token, stmts }),
})
}
}
impl ToTokens for DefaultFn {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
// struct declaration
let typed_fields = self
.sig
.inputs_with_defaults
.iter()
.map(|arg| match &*arg.pat {
syn::Pat::Ident(syn::PatIdent { ident, .. }) => {
let arg_name = ident;
let arg_type = &arg.ty;
quote! { #arg_name: #arg_type }
}
_ => {
/* handled in parsing */
unreachable!()
}
});
let field_with_defaults = self
.sig
.inputs_with_defaults
.iter()
.map(|arg| match &*arg.pat {
syn::Pat::Ident(syn::PatIdent {
ident: field_name, ..
}) => {
let val = &arg.default_value;
quote! { #field_name: #val }
}
_ => {
/* handled in parsing */
unreachable!()
}
});
// the struct's (and it's field's) visibility is taken from function's
// so that you can use generated struct where you can use the function.
let vis = &self.vis;
let struct_name =
quote::format_ident!("{}Args", capitalize(&mut self.sig.ident.to_string()),);
let generics_of_default_args = self.sig.inputs_with_defaults.pairs().filter_map(|p| {
if self.sig.generics.params.pairs().any(|ty_par| {
*match ty_par.value() {
syn::GenericParam::Type(ty) => &ty.ident,
syn::GenericParam::Lifetime(lt) => <.lifetime.ident,
syn::GenericParam::Const(co) => &co.ident,
} == p.value().ty.to_token_stream().to_string()
}) {
Some(&p.value().ty)
} else {
None
}
});
let where_clause = &self.sig.generics.where_clause;
let generics_decl = generics_of_default_args.clone();
let strukt = quote! {
#[allow(unused)]
// #[allow(non_camel_case_types)]
#vis struct #struct_name <#(#generics_decl),*> #where_clause {
#vis #(#typed_fields),*
}
};
tokens.extend([strukt]);
let generics_decl = generics_of_default_args.clone();
let default_impl = quote! {
impl <#(#generics_decl),*>::core::default::Default for #struct_name <#(#generics_of_default_args),*> #where_clause {
fn default() -> Self {
Self {
#(#field_with_defaults),*
}
}
}
};
tokens.extend([default_impl]);
// function declaration
tokens.append_all(outer_attributes(&self.attrs));
self.vis.to_tokens(tokens);
self.sig.to_tokens(tokens);
self.block.brace_token.surround(tokens, |tokens| {
tokens.append_all(inner_attributes(&self.attrs));
tokens.append_all(&self.block.stmts);
});
}
}
fn single_parse_inner(input: parse::ParseStream) -> syn::Result<syn::Attribute> {
let content: parse::ParseBuffer;
let bracket_token: syn::token::Bracket = syn::bracketed!(content in input);
let path: syn::Path = content.call(syn::Path::parse_mod_style)?;
let tokens = content.parse()?;
Ok(syn::Attribute {
pound_token: input.parse()?,
style: syn::AttrStyle::Inner(input.parse()?),
bracket_token,
path,
tokens,
})
}
#[derive(Debug)]
pub struct KeyWordSignature {
pub constness: Option<Token![const]>,
pub asyncness: Option<Token![async]>,
pub unsafety: Option<Token![unsafe]>,
pub abi: Option<syn::Abi>,
pub fn_token: Token![fn],
pub ident: proc_macro2::Ident,
pub generics: syn::Generics,
pub paren_token: syn::token::Paren,
pub inputs: punctuated::Punctuated<SimpleFnArg, Token![,]>,
pub inputs_with_defaults: punctuated::Punctuated<FnArgWithDefault, Token![,]>,
pub output: syn::ReturnType,
}
impl parse::Parse for KeyWordSignature {
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
let constness: Option<Token![const]> = input.parse()?;
let asyncness: Option<Token![async]> = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let abi: Option<syn::Abi> = input.parse()?;
let fn_token: Token![fn] = input.parse()?;
let ident: proc_macro2::Ident = input.parse()?;
let mut generics: syn::Generics = input.parse()?;
let content: parse::ParseBuffer;
let paren_token: syn::token::Paren = syn::parenthesized!(content in input);
let inputs = parse_fn_args(&content)?;
let inputs_with_defaults = parse_fn_args_with_defaults(&content)?;
let output: syn::ReturnType = input.parse()?;
generics.where_clause = input.parse()?;
Ok(Self {
constness,
asyncness,
unsafety,
abi,
fn_token,
ident,
generics,
paren_token,
inputs,
inputs_with_defaults,
output,
})
}
}
impl ToTokens for KeyWordSignature {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.constness.to_tokens(tokens);
self.asyncness.to_tokens(tokens);
self.unsafety.to_tokens(tokens);
self.abi.to_tokens(tokens);
self.fn_token.to_tokens(tokens);
self.ident.to_tokens(tokens);
self.generics.to_tokens(tokens);
self.paren_token.surround(tokens, |tokens| {
for input in self.inputs.pairs() {
match input {
punctuated::Pair::Punctuated(input, comma) => {
input.to_tokens(tokens);
comma.to_tokens(tokens);
}
punctuated::Pair::End(input) => {
input.to_tokens(tokens);
<Token![,]>::default().to_tokens(tokens);
}
}
}
let struct_name =
quote::format_ident!("{}Args", capitalize(&mut self.ident.to_string()));
let fields = self.inputs_with_defaults.iter().map(|arg| match &*arg.pat {
syn::Pat::Ident(syn::PatIdent {
ident: field_name,
attrs,
by_ref,
mutability,
subpat: _,
}) => {
quote! { #(#attrs),* #by_ref #mutability #field_name }
}
_ => {
/* handled in parsing */
unreachable!()
}
});
let generics_of_default_args = self.inputs_with_defaults.pairs().filter_map(|p| {
if self.generics.params.pairs().any(|ty_par| {
*match ty_par.value() {
syn::GenericParam::Type(ty) => &ty.ident,
syn::GenericParam::Lifetime(lt) => <.lifetime.ident,
syn::GenericParam::Const(co) => &co.ident,
} == p.value().ty.to_token_stream().to_string()
}) {
Some(&p.value().ty)
} else {
None
}
});
let arg =
quote! { #struct_name { #(#fields),* }: #struct_name <#(#generics_of_default_args),*> };
tokens.extend([arg]);
});
self.output.to_tokens(tokens);
self.generics.where_clause.to_tokens(tokens);
}
}
#[derive(Debug)]
pub struct SimpleFnArg {
pub attrs: Vec<syn::Attribute>,
pub pat: Box<syn::Pat>,
pub colon_token: Token![:],
pub ty: Box<syn::Type>,
}
impl parse::Parse for SimpleFnArg {
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
let attrs: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?;
let pat: Box<syn::Pat> = Box::new(multi_pat(input)?);
let colon_token: Token![:] = input.parse()?;
let ty: Box<syn::Type> = Box::new(input.parse()?);
Ok(Self {
attrs,
pat,
colon_token,
ty,
})
}
}
impl quote::ToTokens for SimpleFnArg {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.append_all(outer_attributes(&self.attrs));
self.pat.to_tokens(tokens);
self.colon_token.to_tokens(tokens);
self.ty.to_tokens(tokens);
}
}
#[derive(Debug)]
pub struct FnArgWithDefault {
pub pat: Box<syn::Pat>,
pub colon_token: Token![:],
pub ty: Box<syn::Type>,
pub eq_token: Token![=],
pub default_value: Box<syn::Expr>,
}
impl parse::Parse for FnArgWithDefault {
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
let pat: Box<syn::Pat> = Box::new(multi_pat(input)?);
// TODO: unused, remove this
match &*pat {
syn::Pat::Ident(_) => { /* all good */ }
_ => return Err(syn::Error::new_spanned(pat, "pattern is not an ident")),
}
Ok(Self {
pat,
colon_token: input.parse()?,
ty: Box::new(input.parse()?),
eq_token: input.parse()?,
default_value: Box::new(input.parse()?),
})
}
}
fn parse_fn_args_with_defaults(
input: parse::ParseStream,
) -> syn::Result<punctuated::Punctuated<FnArgWithDefault, Token![,]>> {
let mut args = punctuated::Punctuated::new();
while !input.is_empty() {
let arg = input.parse()?;
args.push_value(arg);
if input.is_empty() {
break;
}
let comma: Token![,] = input.parse()?;
args.push_punct(comma);
}
Ok(args)
}
fn parse_fn_args(
input: parse::ParseStream,
) -> syn::Result<punctuated::Punctuated<SimpleFnArg, Token![,]>> {
let mut args = punctuated::Punctuated::new();
while !input.is_empty() {
let attrs: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?;
let ahead: parse::ParseBuffer = input.fork();
if let Ok(Some(dots)) = ahead.parse::<Option<Token![...]>>() {
return Err(syn::Error::new_spanned(
dots,
"variadics are not supported by `default_args` macro",
));
}
let ahead: parse::ParseBuffer = input.fork();
if ahead.parse::<FnArgWithDefault>().is_ok() {
break;
}
let mut arg: SimpleFnArg = input.parse()?;
arg.attrs = attrs;
args.push_value(arg);
if input.is_empty() {
break;
}
let comma: Token![,] = input.parse()?;
args.push_punct(comma);
}
Ok(args)
}
fn multi_pat(input: parse::ParseStream) -> syn::Result<syn::Pat> {
let mut pat: syn::Pat = input.parse()?;
if input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=]) {
let mut cases = punctuated::Punctuated::new();
cases.push_value(pat);
while input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=]) {
let punct = input.parse()?;
cases.push_punct(punct);
let pat: syn::Pat = input.parse()?;
cases.push_value(pat);
}
pat = syn::Pat::Or(syn::PatOr {
attrs: Vec::new(),
leading_vert: None,
cases,
});
}
Ok(pat)
}
impl ToTokens for FnArgWithDefault {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.pat.to_tokens(tokens);
self.colon_token.to_tokens(tokens);
self.ty.to_tokens(tokens);
let _ = self.eq_token;
// default_value is used in the Arg struct's Default impl.
}
}