Skip to content

Commit f86cd0f

Browse files
authored
Merge pull request #4350 from epage/option
feat(derive): Support `Option` when flattening
2 parents 232d91b + 78676f5 commit f86cd0f

File tree

4 files changed

+142
-27
lines changed

4 files changed

+142
-27
lines changed

clap_derive/src/derives/args.rs

+76-11
Original file line numberDiff line numberDiff line change
@@ -202,23 +202,27 @@ pub fn gen_augment(
202202
#implicit_methods;
203203
})
204204
}
205-
Kind::Flatten => {
206-
let ty = &field.ty;
205+
Kind::Flatten(ty) => {
206+
let inner_type = match (**ty, sub_type(&field.ty)) {
207+
(Ty::Option, Some(sub_type)) => sub_type,
208+
_ => &field.ty,
209+
};
210+
207211
let next_help_heading = item.next_help_heading();
208212
let next_display_order = item.next_display_order();
209213
if override_required {
210214
Some(quote_spanned! { kind.span()=>
211215
let #app_var = #app_var
212216
#next_help_heading
213217
#next_display_order;
214-
let #app_var = <#ty as clap::Args>::augment_args_for_update(#app_var);
218+
let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var);
215219
})
216220
} else {
217221
Some(quote_spanned! { kind.span()=>
218222
let #app_var = #app_var
219223
#next_help_heading
220224
#next_display_order;
221-
let #app_var = <#ty as clap::Args>::augment_args(#app_var);
225+
let #app_var = <#inner_type as clap::Args>::augment_args(#app_var);
222226
})
223227
}
224228
}
@@ -350,7 +354,7 @@ pub fn gen_augment(
350354
.iter()
351355
.filter(|(_field, item)| {
352356
let kind = item.kind();
353-
matches!(*kind, Kind::Flatten)
357+
matches!(*kind, Kind::Flatten(_))
354358
})
355359
.count();
356360
if 0 < possible_group_members_len {
@@ -410,14 +414,14 @@ pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
410414
}
411415
}
412416
},
413-
Ty::Vec |
414417
Ty::Other => {
415418
quote_spanned! { kind.span()=>
416419
#field_name: {
417420
<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
418421
}
419422
}
420423
},
424+
Ty::Vec |
421425
Ty::OptionOption |
422426
Ty::OptionVec => {
423427
abort!(
@@ -429,8 +433,42 @@ pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
429433
}
430434
}
431435

432-
Kind::Flatten => quote_spanned! { kind.span()=>
433-
#field_name: clap::FromArgMatches::from_arg_matches_mut(#arg_matches)?
436+
Kind::Flatten(ty) => {
437+
let inner_type = match (**ty, sub_type(&field.ty)) {
438+
(Ty::Option, Some(sub_type)) => sub_type,
439+
_ => &field.ty,
440+
};
441+
match **ty {
442+
Ty::Other => {
443+
quote_spanned! { kind.span()=>
444+
#field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
445+
}
446+
},
447+
Ty::Option => {
448+
quote_spanned! { kind.span()=>
449+
#field_name: {
450+
let group_id = <#inner_type as clap::Args>::group_id()
451+
.expect("`#[arg(flatten)]`ed field type implements `Args::group_id`");
452+
if #arg_matches.contains_id(group_id.as_str()) {
453+
Some(
454+
<#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
455+
)
456+
} else {
457+
None
458+
}
459+
}
460+
}
461+
},
462+
Ty::Vec |
463+
Ty::OptionOption |
464+
Ty::OptionVec => {
465+
abort!(
466+
ty.span(),
467+
"{} types are not supported for flatten",
468+
ty.as_str()
469+
);
470+
}
471+
}
434472
},
435473

436474
Kind::Skip(val, _) => match val {
@@ -506,9 +544,36 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
506544
}
507545
}
508546

509-
Kind::Flatten => quote_spanned! { kind.span()=> {
510-
#access
511-
clap::FromArgMatches::update_from_arg_matches_mut(#field_name, #arg_matches)?;
547+
Kind::Flatten(ty) => {
548+
let inner_type = match (**ty, sub_type(&field.ty)) {
549+
(Ty::Option, Some(sub_type)) => sub_type,
550+
_ => &field.ty,
551+
};
552+
553+
let updater = quote_spanned! { ty.span()=>
554+
<#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
555+
};
556+
557+
let updater = match **ty {
558+
Ty::Option => quote_spanned! { kind.span()=>
559+
if let Some(#field_name) = #field_name.as_mut() {
560+
#updater
561+
} else {
562+
*#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut(
563+
#arg_matches
564+
)?);
565+
}
566+
},
567+
_ => quote_spanned! { kind.span()=>
568+
#updater
569+
},
570+
};
571+
572+
quote_spanned! { kind.span()=>
573+
{
574+
#access
575+
#updater
576+
}
512577
}
513578
},
514579

clap_derive/src/derives/subcommand.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ fn gen_augment(
173173
Some(subcommand)
174174
}
175175

176-
Kind::Flatten => match variant.fields {
176+
Kind::Flatten(_) => match variant.fields {
177177
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
178178
let ty = &unnamed[0];
179179
let deprecations = if !override_required {
@@ -363,7 +363,7 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
363363
})
364364
.partition(|(_, item)| {
365365
let kind = item.kind();
366-
matches!(&*kind, Kind::Flatten)
366+
matches!(&*kind, Kind::Flatten(_))
367367
});
368368

369369
let subcommands = variants.iter().map(|(_variant, item)| {
@@ -464,7 +464,7 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
464464
})
465465
.partition(|(_, item)| {
466466
let kind = item.kind();
467-
matches!(&*kind, Kind::Flatten)
467+
matches!(&*kind, Kind::Flatten(_))
468468
});
469469

470470
let subcommands = variants.iter().map(|(variant, item)| {
@@ -571,7 +571,7 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
571571
})
572572
.partition(|(_, item)| {
573573
let kind = item.kind();
574-
matches!(&*kind, Kind::Flatten)
574+
matches!(&*kind, Kind::Flatten(_))
575575
});
576576

577577
let subcommands = variants.iter().map(|(variant, item)| {

clap_derive/src/item.rs

+19-12
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl Item {
148148
}
149149

150150
match &*res.kind {
151-
Kind::Flatten => {
151+
Kind::Flatten(_) => {
152152
if res.has_explicit_methods() {
153153
abort!(
154154
res.kind.span(),
@@ -224,7 +224,7 @@ impl Item {
224224
}
225225

226226
match &*res.kind {
227-
Kind::Flatten => {
227+
Kind::Flatten(_) => {
228228
if res.has_explicit_methods() {
229229
abort!(
230230
res.kind.span(),
@@ -383,7 +383,12 @@ impl Item {
383383
let expr = attr.value_or_abort();
384384
abort!(expr, "attribute `{}` does not accept a value", attr.name);
385385
}
386-
let kind = Sp::new(Kind::Flatten, attr.name.clone().span());
386+
let ty = self
387+
.kind()
388+
.ty()
389+
.cloned()
390+
.unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
391+
let kind = Sp::new(Kind::Flatten(ty), attr.name.clone().span());
387392
Some(kind)
388393
}
389394
Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => {
@@ -902,10 +907,10 @@ impl Item {
902907
match (self.kind.get(), kind.get()) {
903908
(Kind::Arg(_), Kind::FromGlobal(_))
904909
| (Kind::Arg(_), Kind::Subcommand(_))
905-
| (Kind::Arg(_), Kind::Flatten)
910+
| (Kind::Arg(_), Kind::Flatten(_))
906911
| (Kind::Arg(_), Kind::Skip(_, _))
907912
| (Kind::Command(_), Kind::Subcommand(_))
908-
| (Kind::Command(_), Kind::Flatten)
913+
| (Kind::Command(_), Kind::Flatten(_))
909914
| (Kind::Command(_), Kind::Skip(_, _))
910915
| (Kind::Command(_), Kind::ExternalSubcommand)
911916
| (Kind::Value, Kind::Skip(_, _)) => {
@@ -1142,7 +1147,7 @@ pub enum Kind {
11421147
Value,
11431148
FromGlobal(Sp<Ty>),
11441149
Subcommand(Sp<Ty>),
1145-
Flatten,
1150+
Flatten(Sp<Ty>),
11461151
Skip(Option<AttrValue>, AttrKind),
11471152
ExternalSubcommand,
11481153
}
@@ -1155,7 +1160,7 @@ impl Kind {
11551160
Self::Value => "value",
11561161
Self::FromGlobal(_) => "from_global",
11571162
Self::Subcommand(_) => "subcommand",
1158-
Self::Flatten => "flatten",
1163+
Self::Flatten(_) => "flatten",
11591164
Self::Skip(_, _) => "skip",
11601165
Self::ExternalSubcommand => "external_subcommand",
11611166
}
@@ -1168,18 +1173,20 @@ impl Kind {
11681173
Self::Value => AttrKind::Value,
11691174
Self::FromGlobal(_) => AttrKind::Arg,
11701175
Self::Subcommand(_) => AttrKind::Command,
1171-
Self::Flatten => AttrKind::Command,
1176+
Self::Flatten(_) => AttrKind::Command,
11721177
Self::Skip(_, kind) => *kind,
11731178
Self::ExternalSubcommand => AttrKind::Command,
11741179
}
11751180
}
11761181

11771182
pub fn ty(&self) -> Option<&Sp<Ty>> {
11781183
match self {
1179-
Self::Arg(ty) | Self::Command(ty) | Self::FromGlobal(ty) | Self::Subcommand(ty) => {
1180-
Some(ty)
1181-
}
1182-
Self::Value | Self::Flatten | Self::Skip(_, _) | Self::ExternalSubcommand => None,
1184+
Self::Arg(ty)
1185+
| Self::Command(ty)
1186+
| Self::Flatten(ty)
1187+
| Self::FromGlobal(ty)
1188+
| Self::Subcommand(ty) => Some(ty),
1189+
Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None,
11831190
}
11841191
}
11851192
}

tests/derive/flatten.rs

+43
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,46 @@ fn docstrings_ordering_with_multiple_clap_partial() {
255255

256256
assert!(short_help.contains("This is the docstring for Flattened"));
257257
}
258+
259+
#[test]
260+
fn optional_flatten() {
261+
#[derive(Parser, Debug, PartialEq, Eq)]
262+
struct Opt {
263+
#[command(flatten)]
264+
source: Option<Source>,
265+
}
266+
267+
#[derive(clap::Args, Debug, PartialEq, Eq)]
268+
struct Source {
269+
crates: Vec<String>,
270+
#[arg(long)]
271+
path: Option<std::path::PathBuf>,
272+
#[arg(long)]
273+
git: Option<String>,
274+
}
275+
276+
assert_eq!(
277+
Opt { source: None },
278+
Opt::try_parse_from(&["test"]).unwrap()
279+
);
280+
assert_eq!(
281+
Opt {
282+
source: Some(Source {
283+
crates: vec!["serde".to_owned()],
284+
path: None,
285+
git: None,
286+
}),
287+
},
288+
Opt::try_parse_from(&["test", "serde"]).unwrap()
289+
);
290+
assert_eq!(
291+
Opt {
292+
source: Some(Source {
293+
crates: Vec::new(),
294+
path: Some("./".into()),
295+
git: None,
296+
}),
297+
},
298+
Opt::try_parse_from(&["test", "--path=./"]).unwrap()
299+
);
300+
}

0 commit comments

Comments
 (0)