1 | // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>, |
2 | // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and |
3 | // Ana Hobden (@hoverbear) <operator@hoverbear.org> |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | // |
11 | // This work was derived from Structopt (https://github.com/TeXitoi/structopt) |
12 | // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the |
13 | // MIT/Apache 2.0 license. |
14 | |
15 | use proc_macro2::{Ident, Span, TokenStream}; |
16 | use quote::{format_ident, quote, quote_spanned}; |
17 | use syn::{ |
18 | punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field, |
19 | Fields, FieldsNamed, Generics, |
20 | }; |
21 | |
22 | use crate::item::{Item, Kind, Name}; |
23 | use crate::utils::{inner_type, sub_type, Sp, Ty}; |
24 | |
25 | pub(crate) fn derive_args(input: &DeriveInput) -> Result<TokenStream, syn::Error> { |
26 | let ident = &input.ident; |
27 | |
28 | match input.data { |
29 | Data::Struct(DataStruct { |
30 | fields: Fields::Named(ref fields), |
31 | .. |
32 | }) => { |
33 | let name = Name::Derived(ident.clone()); |
34 | let item = Item::from_args_struct(input, name)?; |
35 | let fields = collect_args_fields(&item, fields)?; |
36 | gen_for_struct(&item, ident, &input.generics, &fields) |
37 | } |
38 | Data::Struct(DataStruct { |
39 | fields: Fields::Unit, |
40 | .. |
41 | }) => { |
42 | let name = Name::Derived(ident.clone()); |
43 | let item = Item::from_args_struct(input, name)?; |
44 | let fields = Punctuated::<Field, Comma>::new(); |
45 | let fields = fields |
46 | .iter() |
47 | .map(|field| { |
48 | let item = Item::from_args_field(field, item.casing(), item.env_casing())?; |
49 | Ok((field, item)) |
50 | }) |
51 | .collect::<Result<Vec<_>, syn::Error>>()?; |
52 | gen_for_struct(&item, ident, &input.generics, &fields) |
53 | } |
54 | _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs" ), |
55 | } |
56 | } |
57 | |
58 | pub(crate) fn gen_for_struct( |
59 | item: &Item, |
60 | item_name: &Ident, |
61 | generics: &Generics, |
62 | fields: &[(&Field, Item)], |
63 | ) -> Result<TokenStream, syn::Error> { |
64 | if !matches!(&*item.kind(), Kind::Command(_)) { |
65 | abort! { item.kind().span(), |
66 | "` {}` cannot be used with `command`" , |
67 | item.kind().name(), |
68 | } |
69 | } |
70 | |
71 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); |
72 | |
73 | let constructor = gen_constructor(fields)?; |
74 | let updater = gen_updater(fields, true)?; |
75 | let raw_deprecated = raw_deprecated(); |
76 | |
77 | let app_var = Ident::new("__clap_app" , Span::call_site()); |
78 | let augmentation = gen_augment(fields, &app_var, item, false)?; |
79 | let augmentation_update = gen_augment(fields, &app_var, item, true)?; |
80 | |
81 | let group_id = if item.skip_group() { |
82 | quote!(None) |
83 | } else { |
84 | let group_id = item.group_id(); |
85 | quote!(Some(clap::Id::from(#group_id))) |
86 | }; |
87 | |
88 | Ok(quote! { |
89 | #[allow( |
90 | dead_code, |
91 | unreachable_code, |
92 | unused_variables, |
93 | unused_braces, |
94 | unused_qualifications, |
95 | )] |
96 | #[allow( |
97 | clippy::style, |
98 | clippy::complexity, |
99 | clippy::pedantic, |
100 | clippy::restriction, |
101 | clippy::perf, |
102 | clippy::deprecated, |
103 | clippy::nursery, |
104 | clippy::cargo, |
105 | clippy::suspicious_else_formatting, |
106 | clippy::almost_swapped, |
107 | clippy::redundant_locals, |
108 | )] |
109 | #[automatically_derived] |
110 | impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause { |
111 | fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> { |
112 | Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) |
113 | } |
114 | |
115 | fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> { |
116 | #raw_deprecated |
117 | let v = #item_name #constructor; |
118 | ::std::result::Result::Ok(v) |
119 | } |
120 | |
121 | fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> { |
122 | self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone()) |
123 | } |
124 | |
125 | fn update_from_arg_matches_mut(&mut self, __clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<(), clap::Error> { |
126 | #raw_deprecated |
127 | #updater |
128 | ::std::result::Result::Ok(()) |
129 | } |
130 | } |
131 | |
132 | #[allow( |
133 | dead_code, |
134 | unreachable_code, |
135 | unused_variables, |
136 | unused_braces, |
137 | unused_qualifications, |
138 | )] |
139 | #[allow( |
140 | clippy::style, |
141 | clippy::complexity, |
142 | clippy::pedantic, |
143 | clippy::restriction, |
144 | clippy::perf, |
145 | clippy::deprecated, |
146 | clippy::nursery, |
147 | clippy::cargo, |
148 | clippy::suspicious_else_formatting, |
149 | clippy::almost_swapped, |
150 | clippy::redundant_locals, |
151 | )] |
152 | #[automatically_derived] |
153 | impl #impl_generics clap::Args for #item_name #ty_generics #where_clause { |
154 | fn group_id() -> Option<clap::Id> { |
155 | #group_id |
156 | } |
157 | fn augment_args<'b>(#app_var: clap::Command) -> clap::Command { |
158 | #augmentation |
159 | } |
160 | fn augment_args_for_update<'b>(#app_var: clap::Command) -> clap::Command { |
161 | #augmentation_update |
162 | } |
163 | } |
164 | }) |
165 | } |
166 | |
167 | /// Generate a block of code to add arguments/subcommands corresponding to |
168 | /// the `fields` to an cmd. |
169 | pub(crate) fn gen_augment( |
170 | fields: &[(&Field, Item)], |
171 | app_var: &Ident, |
172 | parent_item: &Item, |
173 | override_required: bool, |
174 | ) -> Result<TokenStream, syn::Error> { |
175 | let mut subcommand_specified = false; |
176 | let mut args = Vec::new(); |
177 | for (field, item) in fields { |
178 | let kind = item.kind(); |
179 | let genned = match &*kind { |
180 | Kind::Command(_) |
181 | | Kind::Value |
182 | | Kind::Skip(_, _) |
183 | | Kind::FromGlobal(_) |
184 | | Kind::ExternalSubcommand => None, |
185 | Kind::Subcommand(ty) => { |
186 | if subcommand_specified { |
187 | abort!( |
188 | field.span(), |
189 | "`#[command(subcommand)]` can only be used once per container" |
190 | ); |
191 | } |
192 | subcommand_specified = true; |
193 | |
194 | let subcmd_type = match (**ty, sub_type(&field.ty)) { |
195 | (Ty::Option, Some(sub_type)) => sub_type, |
196 | _ => &field.ty, |
197 | }; |
198 | let implicit_methods = if **ty == Ty::Option { |
199 | quote!() |
200 | } else { |
201 | quote_spanned! { kind.span()=> |
202 | .subcommand_required(true) |
203 | .arg_required_else_help(true) |
204 | } |
205 | }; |
206 | |
207 | let override_methods = if override_required { |
208 | quote_spanned! { kind.span()=> |
209 | .subcommand_required(false) |
210 | .arg_required_else_help(false) |
211 | } |
212 | } else { |
213 | quote!() |
214 | }; |
215 | |
216 | Some(quote! { |
217 | let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var ); |
218 | let #app_var = #app_var |
219 | #implicit_methods |
220 | #override_methods; |
221 | }) |
222 | } |
223 | Kind::Flatten(ty) => { |
224 | let inner_type = match (**ty, sub_type(&field.ty)) { |
225 | (Ty::Option, Some(sub_type)) => sub_type, |
226 | _ => &field.ty, |
227 | }; |
228 | |
229 | let next_help_heading = item.next_help_heading(); |
230 | let next_display_order = item.next_display_order(); |
231 | let flatten_group_assert = if matches!(**ty, Ty::Option) { |
232 | quote_spanned! { kind.span()=> |
233 | <#inner_type as clap::Args>::group_id().expect("cannot `#[flatten]` an `Option<Args>` with `#[group(skip)]`" ); |
234 | } |
235 | } else { |
236 | quote! {} |
237 | }; |
238 | if override_required { |
239 | Some(quote_spanned! { kind.span()=> |
240 | #flatten_group_assert |
241 | let #app_var = #app_var |
242 | #next_help_heading |
243 | #next_display_order; |
244 | let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var); |
245 | }) |
246 | } else { |
247 | Some(quote_spanned! { kind.span()=> |
248 | #flatten_group_assert |
249 | let #app_var = #app_var |
250 | #next_help_heading |
251 | #next_display_order; |
252 | let #app_var = <#inner_type as clap::Args>::augment_args(#app_var); |
253 | }) |
254 | } |
255 | } |
256 | Kind::Arg(ty) => { |
257 | let value_parser = item.value_parser(&field.ty); |
258 | let action = item.action(&field.ty); |
259 | let value_name = item.value_name(); |
260 | |
261 | let implicit_methods = match **ty { |
262 | Ty::Unit => { |
263 | // Leaving out `value_parser` as it will always fail |
264 | quote_spanned! { ty.span()=> |
265 | .value_name(#value_name) |
266 | #action |
267 | } |
268 | } |
269 | Ty::Option => { |
270 | quote_spanned! { ty.span()=> |
271 | .value_name(#value_name) |
272 | #value_parser |
273 | #action |
274 | } |
275 | } |
276 | |
277 | Ty::OptionOption => quote_spanned! { ty.span()=> |
278 | .value_name(#value_name) |
279 | .num_args(0..=1) |
280 | #value_parser |
281 | #action |
282 | }, |
283 | |
284 | Ty::OptionVec => { |
285 | if item.is_positional() { |
286 | quote_spanned! { ty.span()=> |
287 | .value_name(#value_name) |
288 | .num_args(1..) // action won't be sufficient for getting multiple |
289 | #value_parser |
290 | #action |
291 | } |
292 | } else { |
293 | quote_spanned! { ty.span()=> |
294 | .value_name(#value_name) |
295 | #value_parser |
296 | #action |
297 | } |
298 | } |
299 | } |
300 | |
301 | Ty::Vec => { |
302 | if item.is_positional() { |
303 | quote_spanned! { ty.span()=> |
304 | .value_name(#value_name) |
305 | .num_args(1..) // action won't be sufficient for getting multiple |
306 | #value_parser |
307 | #action |
308 | } |
309 | } else { |
310 | quote_spanned! { ty.span()=> |
311 | .value_name(#value_name) |
312 | #value_parser |
313 | #action |
314 | } |
315 | } |
316 | } |
317 | |
318 | Ty::VecVec | Ty::OptionVecVec => { |
319 | quote_spanned! { ty.span() => |
320 | .value_name(#value_name) |
321 | #value_parser |
322 | #action |
323 | } |
324 | } |
325 | |
326 | Ty::Other => { |
327 | let required = item.find_default_method().is_none(); |
328 | // `ArgAction::takes_values` is assuming `ArgAction::default_value` will be |
329 | // set though that won't always be true but this should be good enough, |
330 | // otherwise we'll report an "arg required" error when unwrapping. |
331 | let action_value = action.args(); |
332 | quote_spanned! { ty.span()=> |
333 | .value_name(#value_name) |
334 | .required(#required && #action_value.takes_values()) |
335 | #value_parser |
336 | #action |
337 | } |
338 | } |
339 | }; |
340 | |
341 | let id = item.id(); |
342 | let explicit_methods = item.field_methods(); |
343 | let deprecations = if !override_required { |
344 | item.deprecations() |
345 | } else { |
346 | quote!() |
347 | }; |
348 | let override_methods = if override_required { |
349 | quote_spanned! { kind.span()=> |
350 | .required(false) |
351 | } |
352 | } else { |
353 | quote!() |
354 | }; |
355 | |
356 | Some(quote_spanned! { field.span()=> |
357 | let #app_var = #app_var.arg({ |
358 | #deprecations |
359 | |
360 | #[allow(deprecated)] |
361 | let arg = clap::Arg::new(#id) |
362 | #implicit_methods; |
363 | |
364 | let arg = arg |
365 | #explicit_methods; |
366 | |
367 | let arg = arg |
368 | #override_methods; |
369 | |
370 | arg |
371 | }); |
372 | }) |
373 | } |
374 | }; |
375 | args.push(genned); |
376 | } |
377 | |
378 | let deprecations = if !override_required { |
379 | parent_item.deprecations() |
380 | } else { |
381 | quote!() |
382 | }; |
383 | let initial_app_methods = parent_item.initial_top_level_methods(); |
384 | let final_app_methods = parent_item.final_top_level_methods(); |
385 | let group_app_methods = if parent_item.skip_group() { |
386 | quote!() |
387 | } else { |
388 | let group_id = parent_item.group_id(); |
389 | let literal_group_members = fields |
390 | .iter() |
391 | .filter_map(|(_field, item)| { |
392 | let kind = item.kind(); |
393 | if matches!(*kind, Kind::Arg(_)) { |
394 | Some(item.id()) |
395 | } else { |
396 | None |
397 | } |
398 | }) |
399 | .collect::<Vec<_>>(); |
400 | let literal_group_members_len = literal_group_members.len(); |
401 | let mut literal_group_members = quote! {{ |
402 | let members: [clap::Id; #literal_group_members_len] = [#( clap::Id::from(#literal_group_members) ),* ]; |
403 | members |
404 | }}; |
405 | // HACK: Validation isn't ready yet for nested arg groups, so just don't populate the group in |
406 | // that situation |
407 | let possible_group_members_len = fields |
408 | .iter() |
409 | .filter(|(_field, item)| { |
410 | let kind = item.kind(); |
411 | matches!(*kind, Kind::Flatten(_)) |
412 | }) |
413 | .count(); |
414 | if 0 < possible_group_members_len { |
415 | literal_group_members = quote! {{ |
416 | let members: [clap::Id; 0] = []; |
417 | members |
418 | }}; |
419 | } |
420 | |
421 | let group_methods = parent_item.group_methods(); |
422 | |
423 | quote!( |
424 | .group( |
425 | clap::ArgGroup::new(#group_id) |
426 | .multiple(true) |
427 | #group_methods |
428 | .args(#literal_group_members) |
429 | ) |
430 | ) |
431 | }; |
432 | Ok(quote! {{ |
433 | #deprecations |
434 | let #app_var = #app_var |
435 | #initial_app_methods |
436 | #group_app_methods |
437 | ; |
438 | #( #args )* |
439 | #app_var #final_app_methods |
440 | }}) |
441 | } |
442 | |
443 | pub(crate) fn gen_constructor(fields: &[(&Field, Item)]) -> Result<TokenStream, syn::Error> { |
444 | let fields = fields.iter().map(|(field, item)| { |
445 | let field_name = field.ident.as_ref().unwrap(); |
446 | let kind = item.kind(); |
447 | let arg_matches = format_ident!("__clap_arg_matches" ); |
448 | let genned = match &*kind { |
449 | Kind::Command(_) |
450 | | Kind::Value |
451 | | Kind::ExternalSubcommand => { |
452 | abort! { kind.span(), |
453 | "` {}` cannot be used with `arg`" , |
454 | kind.name(), |
455 | } |
456 | } |
457 | Kind::Subcommand(ty) => { |
458 | let subcmd_type = match (**ty, sub_type(&field.ty)) { |
459 | (Ty::Option, Some(sub_type)) => sub_type, |
460 | _ => &field.ty, |
461 | }; |
462 | match **ty { |
463 | Ty::Option => { |
464 | quote_spanned! { kind.span()=> |
465 | #field_name: { |
466 | if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) { |
467 | Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?) |
468 | } else { |
469 | None |
470 | } |
471 | } |
472 | } |
473 | }, |
474 | Ty::Other => { |
475 | quote_spanned! { kind.span()=> |
476 | #field_name: { |
477 | <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? |
478 | } |
479 | } |
480 | }, |
481 | Ty::Unit | |
482 | Ty::Vec | |
483 | Ty::OptionOption | |
484 | Ty::OptionVec | |
485 | Ty::VecVec | |
486 | Ty::OptionVecVec => { |
487 | abort!( |
488 | ty.span(), |
489 | " {} types are not supported for subcommand" , |
490 | ty.as_str() |
491 | ); |
492 | } |
493 | } |
494 | } |
495 | |
496 | Kind::Flatten(ty) => { |
497 | let inner_type = match (**ty, sub_type(&field.ty)) { |
498 | (Ty::Option, Some(sub_type)) => sub_type, |
499 | _ => &field.ty, |
500 | }; |
501 | match **ty { |
502 | Ty::Other => { |
503 | quote_spanned! { kind.span()=> |
504 | #field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? |
505 | } |
506 | }, |
507 | Ty::Option => { |
508 | quote_spanned! { kind.span()=> |
509 | #field_name: { |
510 | let group_id = <#inner_type as clap::Args>::group_id() |
511 | .expect("asserted during `Arg` creation" ); |
512 | if #arg_matches.contains_id(group_id.as_str()) { |
513 | Some( |
514 | <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? |
515 | ) |
516 | } else { |
517 | None |
518 | } |
519 | } |
520 | } |
521 | }, |
522 | Ty::Unit | |
523 | Ty::Vec | |
524 | Ty::OptionOption | |
525 | Ty::OptionVec | |
526 | Ty::VecVec | |
527 | Ty::OptionVecVec => { |
528 | abort!( |
529 | ty.span(), |
530 | " {} types are not supported for flatten" , |
531 | ty.as_str() |
532 | ); |
533 | } |
534 | } |
535 | }, |
536 | |
537 | Kind::Skip(val, _) => match val { |
538 | None => quote_spanned!(kind.span()=> #field_name: Default::default()), |
539 | Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()), |
540 | }, |
541 | |
542 | Kind::Arg(ty) | Kind::FromGlobal(ty) => { |
543 | gen_parsers(item, ty, field_name, field, None)? |
544 | } |
545 | }; |
546 | Ok(genned) |
547 | }).collect::<Result<Vec<_>, syn::Error>>()?; |
548 | |
549 | Ok(quote! {{ |
550 | #( #fields ),* |
551 | }}) |
552 | } |
553 | |
554 | pub(crate) fn gen_updater( |
555 | fields: &[(&Field, Item)], |
556 | use_self: bool, |
557 | ) -> Result<TokenStream, syn::Error> { |
558 | let mut genned_fields = Vec::new(); |
559 | for (field, item) in fields { |
560 | let field_name = field.ident.as_ref().unwrap(); |
561 | let kind = item.kind(); |
562 | |
563 | let access = if use_self { |
564 | quote! { |
565 | #[allow(non_snake_case)] |
566 | let #field_name = &mut self.#field_name; |
567 | } |
568 | } else { |
569 | quote!() |
570 | }; |
571 | let arg_matches = format_ident!("__clap_arg_matches" ); |
572 | |
573 | let genned = match &*kind { |
574 | Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => { |
575 | abort! { kind.span(), |
576 | "` {}` cannot be used with `arg`" , |
577 | kind.name(), |
578 | } |
579 | } |
580 | Kind::Subcommand(ty) => { |
581 | let subcmd_type = match (**ty, sub_type(&field.ty)) { |
582 | (Ty::Option, Some(sub_type)) => sub_type, |
583 | _ => &field.ty, |
584 | }; |
585 | |
586 | let updater = quote_spanned! { ty.span()=> |
587 | <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?; |
588 | }; |
589 | |
590 | let updater = match **ty { |
591 | Ty::Option => quote_spanned! { kind.span()=> |
592 | if let Some(#field_name) = #field_name.as_mut() { |
593 | #updater |
594 | } else { |
595 | *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut( |
596 | #arg_matches |
597 | )?); |
598 | } |
599 | }, |
600 | _ => quote_spanned! { kind.span()=> |
601 | #updater |
602 | }, |
603 | }; |
604 | |
605 | quote_spanned! { kind.span()=> |
606 | { |
607 | #access |
608 | #updater |
609 | } |
610 | } |
611 | } |
612 | |
613 | Kind::Flatten(ty) => { |
614 | let inner_type = match (**ty, sub_type(&field.ty)) { |
615 | (Ty::Option, Some(sub_type)) => sub_type, |
616 | _ => &field.ty, |
617 | }; |
618 | |
619 | let updater = quote_spanned! { ty.span()=> |
620 | <#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?; |
621 | }; |
622 | |
623 | let updater = match **ty { |
624 | Ty::Option => quote_spanned! { kind.span()=> |
625 | if let Some(#field_name) = #field_name.as_mut() { |
626 | #updater |
627 | } else { |
628 | *#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut( |
629 | #arg_matches |
630 | )?); |
631 | } |
632 | }, |
633 | _ => quote_spanned! { kind.span()=> |
634 | #updater |
635 | }, |
636 | }; |
637 | |
638 | quote_spanned! { kind.span()=> |
639 | { |
640 | #access |
641 | #updater |
642 | } |
643 | } |
644 | } |
645 | |
646 | Kind::Skip(_, _) => quote!(), |
647 | |
648 | Kind::Arg(ty) | Kind::FromGlobal(ty) => { |
649 | gen_parsers(item, ty, field_name, field, Some(&access))? |
650 | } |
651 | }; |
652 | genned_fields.push(genned); |
653 | } |
654 | |
655 | Ok(quote! { |
656 | #( #genned_fields )* |
657 | }) |
658 | } |
659 | |
660 | fn gen_parsers( |
661 | item: &Item, |
662 | ty: &Sp<Ty>, |
663 | field_name: &Ident, |
664 | field: &Field, |
665 | update: Option<&TokenStream>, |
666 | ) -> Result<TokenStream, syn::Error> { |
667 | let span = ty.span(); |
668 | let convert_type = inner_type(&field.ty); |
669 | let id = item.id(); |
670 | let get_one = quote_spanned!(span=> remove_one::<#convert_type>); |
671 | let get_many = quote_spanned!(span=> remove_many::<#convert_type>); |
672 | let get_occurrences = quote_spanned!(span=> remove_occurrences::<#convert_type>); |
673 | |
674 | // Give this identifier the same hygiene |
675 | // as the `arg_matches` parameter definition. This |
676 | // allows us to refer to `arg_matches` within a `quote_spanned` block |
677 | let arg_matches = format_ident!("__clap_arg_matches" ); |
678 | |
679 | let field_value = match **ty { |
680 | Ty::Unit => { |
681 | quote_spanned! { ty.span()=> |
682 | () |
683 | } |
684 | } |
685 | |
686 | Ty::Option => { |
687 | quote_spanned! { ty.span()=> |
688 | #arg_matches.#get_one(#id) |
689 | } |
690 | } |
691 | |
692 | Ty::OptionOption => quote_spanned! { ty.span()=> |
693 | if #arg_matches.contains_id(#id) { |
694 | Some( |
695 | #arg_matches.#get_one(#id) |
696 | ) |
697 | } else { |
698 | None |
699 | } |
700 | }, |
701 | |
702 | Ty::OptionVec => quote_spanned! { ty.span()=> |
703 | if #arg_matches.contains_id(#id) { |
704 | Some(#arg_matches.#get_many(#id) |
705 | .map(|v| v.collect::<Vec<_>>()) |
706 | .unwrap_or_else(Vec::new)) |
707 | } else { |
708 | None |
709 | } |
710 | }, |
711 | |
712 | Ty::Vec => { |
713 | quote_spanned! { ty.span()=> |
714 | #arg_matches.#get_many(#id) |
715 | .map(|v| v.collect::<Vec<_>>()) |
716 | .unwrap_or_else(Vec::new) |
717 | } |
718 | } |
719 | |
720 | Ty::VecVec => quote_spanned! { ty.span()=> |
721 | #arg_matches.#get_occurrences(#id) |
722 | .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>()) |
723 | .unwrap_or_else(Vec::new) |
724 | }, |
725 | |
726 | Ty::OptionVecVec => quote_spanned! { ty.span()=> |
727 | #arg_matches.#get_occurrences(#id) |
728 | .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>()) |
729 | }, |
730 | |
731 | Ty::Other => { |
732 | // Prefer `concat` where possible for reduced code size but fallback to `format!` to |
733 | // allow non-literal `id`s |
734 | match id { |
735 | Name::Assigned(_) => { |
736 | quote_spanned! { ty.span()=> |
737 | #arg_matches.#get_one(#id) |
738 | .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}" , #id)))? |
739 | } |
740 | } |
741 | Name::Derived(_) => { |
742 | quote_spanned! { ty.span()=> |
743 | #arg_matches.#get_one(#id) |
744 | .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, concat!("The following required argument was not provided: " , #id)))? |
745 | } |
746 | } |
747 | } |
748 | } |
749 | }; |
750 | |
751 | let genned = if let Some(access) = update { |
752 | quote_spanned! { field.span()=> |
753 | if #arg_matches.contains_id(#id) { |
754 | #access |
755 | *#field_name = #field_value |
756 | } |
757 | } |
758 | } else { |
759 | quote_spanned!(field.span()=> #field_name: #field_value ) |
760 | }; |
761 | Ok(genned) |
762 | } |
763 | |
764 | #[cfg (feature = "raw-deprecated" )] |
765 | pub(crate) fn raw_deprecated() -> TokenStream { |
766 | quote! {} |
767 | } |
768 | |
769 | #[cfg (not(feature = "raw-deprecated" ))] |
770 | pub(crate) fn raw_deprecated() -> TokenStream { |
771 | quote! { |
772 | #![allow(deprecated)] // Assuming any deprecation in here will be related to a deprecation in `Args` |
773 | |
774 | } |
775 | } |
776 | |
777 | pub(crate) fn collect_args_fields<'a>( |
778 | item: &'a Item, |
779 | fields: &'a FieldsNamed, |
780 | ) -> Result<Vec<(&'a Field, Item)>, syn::Error> { |
781 | fieldsimpl Iterator- >
|
782 | .named |
783 | .iter() |
784 | .map(|field: &Field| { |
785 | let item: Item = Item::from_args_field(field, struct_casing:item.casing(), item.env_casing())?; |
786 | Ok((field, item)) |
787 | }) |
788 | .collect() |
789 | } |
790 | |