1use crate::{
2 arg::ArgType,
3 error::{ArgsError, ArgsErrorKind, ArgsErrorWithInput, get_variants_from_shape},
4 help::{HelpConfig, generate_help_for_shape},
5 span::Span,
6};
7use facet_core::{Def, EnumType, Facet, Field, Shape, Type, UserType, Variant};
8use facet_reflect::{HeapValue, Partial};
9use heck::{ToKebabCase, ToSnakeCase};
10
11fn is_help_flag(arg: &str) -> bool {
13 matches!(arg, "-h" | "--help" | "-help" | "/?")
14}
15
16pub fn from_std_args<T: Facet<'static>>() -> Result<T, ArgsErrorWithInput> {
18 let args = std::env::args().skip(1).collect::<Vec<String>>();
19 let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
20 from_slice(&args_str[..])
21}
22
23pub fn from_slice<'input, T: Facet<'static>>(
25 args: &'input [&'input str],
26) -> Result<T, ArgsErrorWithInput> {
27 from_slice_with_config(args, &HelpConfig::default())
28}
29
30pub fn from_slice_with_config<'input, T: Facet<'static>>(
32 args: &'input [&'input str],
33 help_config: &HelpConfig,
34) -> Result<T, ArgsErrorWithInput> {
35 if let Some(first_arg) = args.first()
37 && is_help_flag(first_arg)
38 {
39 let help_text = generate_help_for_shape(T::SHAPE, help_config);
40 let span = Span::new(0, first_arg.len());
41 return Err(ArgsErrorWithInput {
42 inner: ArgsError::new(ArgsErrorKind::HelpRequested { help_text }, span),
43 flattened_args: args.join(" "),
44 });
45 }
46
47 let mut cx = Context::new(args, T::SHAPE);
48 let hv = cx.work_add_input()?;
49
50 Ok(hv.materialize::<T>().unwrap())
52}
53
54struct Context<'input> {
55 shape: &'static Shape,
57
58 args: &'input [&'input str],
60
61 index: usize,
63
64 positional_only: bool,
66
67 arg_indices: Vec<usize>,
69
70 flattened_args: String,
72}
73
74impl<'input> Context<'input> {
75 fn new(args: &'input [&'input str], shape: &'static Shape) -> Self {
76 let mut arg_indices = vec![];
77 let mut flattened_args = String::new();
78
79 for arg in args {
80 arg_indices.push(flattened_args.len());
81 flattened_args.push_str(arg);
82 flattened_args.push(' ');
83 }
84 tracing::trace!("flattened args: {flattened_args:?}");
85 tracing::trace!("arg_indices: {arg_indices:?}");
86
87 Self {
88 shape,
89 args,
90 index: 0,
91 positional_only: false,
92 arg_indices,
93 flattened_args,
94 }
95 }
96
97 fn fields(&self, p: &Partial<'static>) -> Result<&'static [Field], ArgsErrorKind> {
99 let shape = p.shape();
100 match &shape.ty {
101 Type::User(UserType::Struct(struct_type)) => Ok(struct_type.fields),
102 _ => Err(ArgsErrorKind::NoFields { shape }),
103 }
104 }
105
106 fn handle_field(
109 &mut self,
110 p: Partial<'static>,
111 field_index: usize,
112 value: Option<SplitToken<'input>>,
113 ) -> Result<Partial<'static>, ArgsErrorKind> {
114 tracing::trace!("Handling field at index {field_index}");
115
116 let mut p = p.begin_nth_field(field_index)?;
117
118 tracing::trace!("After begin_field, shape is {}", p.shape());
119 if p.shape().is_shape(bool::SHAPE) {
120 let bool_value = if let Some(value) = value {
122 match value.s.to_lowercase().as_str() {
124 "true" | "yes" | "1" | "on" => true,
125 "false" | "no" | "0" | "off" => false,
126 "" => true, other => {
128 tracing::warn!("Unknown boolean value '{other}', treating as true");
129 true
130 }
131 }
132 } else {
133 true
135 };
136 tracing::trace!("Flag is boolean, setting it to {bool_value}");
137 p = p.set(bool_value)?;
138
139 self.index += 1;
140 } else {
141 tracing::trace!("Flag isn't boolean, expecting a {} value", p.shape());
142
143 if let Some(value) = value {
144 p = self.handle_value(p, value.s)?;
145 } else {
146 if self.index + 1 >= self.args.len() {
147 return Err(ArgsErrorKind::ExpectedValueGotEof { shape: p.shape() });
148 }
149 let value = self.args[self.index + 1];
150
151 self.index += 1;
152 p = self.handle_value(p, value)?;
153 }
154
155 self.index += 1;
156 }
157
158 p = p.end()?;
159
160 Ok(p)
161 }
162
163 fn handle_value(
164 &mut self,
165 p: Partial<'static>,
166 value: &'input str,
167 ) -> Result<Partial<'static>, ArgsErrorKind> {
168 if let Type::User(UserType::Enum(_)) = p.shape().ty {
171 return Err(ArgsErrorKind::ReflectError(
174 facet_reflect::ReflectError::OperationFailed {
175 shape: p.shape(),
176 operation: "Subcommands must be provided as positional arguments, not as flag values. Use the subcommand name directly instead of --flag <value>.",
177 },
178 ));
179 }
180
181 let p = match p.shape().def {
182 Def::List(_) => {
183 let mut p = p.begin_list()?;
185 p = p.begin_list_item()?;
186 p = p.parse_from_str(value)?;
187 p.end()?
188 }
189 Def::Option(_) => {
190 let mut p = p.begin_some()?;
192 p = p.parse_from_str(value)?;
193 p.end()?
194 }
195 _ => {
196 p.parse_from_str(value)?
198 }
199 };
200
201 Ok(p)
202 }
203
204 fn work_add_input(&mut self) -> Result<HeapValue<'static>, ArgsErrorWithInput> {
205 self.work().map_err(|e| ArgsErrorWithInput {
206 inner: e,
207 flattened_args: self.flattened_args.clone(),
208 })
209 }
210
211 fn work(&mut self) -> Result<HeapValue<'static>, ArgsError> {
213 self.work_inner().map_err(|kind| {
214 let span = kind.precise_span().unwrap_or_else(|| {
216 if self.index >= self.args.len() {
217 Span::new(self.flattened_args.len(), 0)
218 } else {
219 let arg = self.args[self.index];
220 let index = self.arg_indices[self.index];
221 Span::new(index, arg.len())
222 }
223 });
224 ArgsError::new(kind, span)
225 })
226 }
227
228 fn work_inner(&mut self) -> Result<HeapValue<'static>, ArgsErrorKind> {
229 let p = Partial::alloc_shape(self.shape)?;
230
231 match self.shape.ty {
234 Type::User(UserType::Struct(_)) => self.parse_struct(p),
235 Type::User(UserType::Enum(_)) => {
236 Err(ArgsErrorKind::ReflectError(
238 facet_reflect::ReflectError::OperationFailed {
239 shape: self.shape,
240 operation: "Top-level enums must be wrapped in a struct with #[facet(args::subcommand)] attribute to be used as subcommands.",
241 },
242 ))
243 }
244 _ => Err(ArgsErrorKind::NoFields { shape: self.shape }),
245 }
246 }
247
248 fn parse_struct(
250 &mut self,
251 mut p: Partial<'static>,
252 ) -> Result<HeapValue<'static>, ArgsErrorKind> {
253 while self.args.len() > self.index {
254 let arg = self.args[self.index];
255 let arg_span = Span::new(self.arg_indices[self.index], arg.len());
256 let at = if self.positional_only {
257 ArgType::Positional
258 } else {
259 ArgType::parse(arg)
260 };
261 tracing::trace!("Parsed {at:?}");
262
263 match at {
264 ArgType::DoubleDash => {
265 self.positional_only = true;
266 self.index += 1;
267 }
268 ArgType::LongFlag(flag) => {
269 if flag.starts_with('-') {
271 let fields = self.fields(&p)?;
272 return Err(ArgsErrorKind::UnknownLongFlag {
273 flag: flag.to_string(),
274 fields,
275 });
276 }
277
278 let flag_span = Span::new(arg_span.start + 2, arg_span.len - 2);
279 match split(flag, flag_span) {
280 Some(tokens) => {
281 let mut tokens = tokens.into_iter();
283 let Some(key) = tokens.next() else {
284 unreachable!()
285 };
286 let Some(value) = tokens.next() else {
287 unreachable!()
288 };
289
290 let flag = key.s;
291 let snek = key.s.to_snake_case();
292 tracing::trace!("Looking up long flag {flag} (field name: {snek})");
293 let fields = self.fields(&p)?;
294 let Some(field_index) = p.field_index(&snek) else {
295 return Err(ArgsErrorKind::UnknownLongFlag {
296 flag: flag.to_string(),
297 fields,
298 });
299 };
300 p = self.handle_field(p, field_index, Some(value))?;
301 }
302 None => {
303 let snek = flag.to_snake_case();
304 tracing::trace!("Looking up long flag {flag} (field name: {snek})");
305 let fields = self.fields(&p)?;
306 let Some(field_index) = p.field_index(&snek) else {
307 return Err(ArgsErrorKind::UnknownLongFlag {
308 flag: flag.to_string(),
309 fields,
310 });
311 };
312 p = self.handle_field(p, field_index, None)?;
313 }
314 }
315 }
316 ArgType::ShortFlag(flag) => {
317 let flag_span = Span::new(arg_span.start + 1, arg_span.len - 1);
318 match split(flag, flag_span) {
319 Some(tokens) => {
320 let mut tokens = tokens.into_iter();
322 let Some(key) = tokens.next() else {
323 unreachable!()
324 };
325 let Some(value) = tokens.next() else {
326 unreachable!()
327 };
328
329 let short_char = key.s;
330 tracing::trace!("Looking up short flag {short_char}");
331 let fields = self.fields(&p)?;
332 let Some(field_index) =
333 find_field_index_with_short_char(fields, short_char)
334 else {
335 return Err(ArgsErrorKind::UnknownShortFlag {
336 flag: short_char.to_string(),
337 fields,
338 precise_span: None, });
340 };
341 p = self.handle_field(p, field_index, Some(value))?;
342 }
343 None => {
344 let fields = self.fields(&p)?;
346 p = self.process_short_flag(p, flag, flag_span, fields)?;
347 }
348 }
349 }
350 ArgType::Positional => {
351 let fields = self.fields(&p)?;
352
353 if let Some((field_index, field)) = find_subcommand_field(fields)
355 && !p.is_field_set(field_index)?
356 {
357 p = self.handle_subcommand_field(p, field_index, field)?;
358 continue;
359 }
360
361 let mut chosen_field_index: Option<usize> = None;
363
364 for (field_index, field) in fields.iter().enumerate() {
365 let is_positional = field.has_attr(Some("args"), "positional");
366 if !is_positional {
367 continue;
368 }
369
370 if matches!(field.shape().def, Def::List(_list_def)) {
373 } else if p.is_field_set(field_index)? {
375 continue;
377 }
378
379 tracing::trace!("found field, it's not a list {field:?}");
380 chosen_field_index = Some(field_index);
381 break;
382 }
383
384 let Some(chosen_field_index) = chosen_field_index else {
385 return Err(ArgsErrorKind::UnexpectedPositionalArgument { fields });
386 };
387
388 p = p.begin_nth_field(chosen_field_index)?;
389
390 let value = self.args[self.index];
391
392 if let Type::User(UserType::Enum(_)) = fields[chosen_field_index].shape().ty
394 && !fields[chosen_field_index].has_attr(Some("args"), "subcommand")
395 {
396 return Err(ArgsErrorKind::EnumWithoutSubcommandAttribute {
397 field: &fields[chosen_field_index],
398 });
399 }
400
401 p = self.handle_value(p, value)?;
402
403 p = p.end()?;
404 self.index += 1;
405 }
406 ArgType::None => todo!(),
407 }
408 }
409
410 p = self.finalize_struct(p)?;
412
413 Ok(p.build()?)
414 }
415
416 fn parse_variant_fields(
418 &mut self,
419 mut p: Partial<'static>,
420 variant: &'static Variant,
421 ) -> Result<Partial<'static>, ArgsErrorKind> {
422 let fields = variant.data.fields;
423
424 while self.args.len() > self.index {
425 let arg = self.args[self.index];
426 let arg_span = Span::new(self.arg_indices[self.index], arg.len());
427 let at = if self.positional_only {
428 ArgType::Positional
429 } else {
430 ArgType::parse(arg)
431 };
432 tracing::trace!("Parsing variant field, arg: {at:?}");
433
434 match at {
435 ArgType::DoubleDash => {
436 self.positional_only = true;
437 self.index += 1;
438 }
439 ArgType::LongFlag(flag) => {
440 if flag.starts_with('-') {
442 return Err(ArgsErrorKind::UnknownLongFlag {
443 flag: flag.to_string(),
444 fields,
445 });
446 }
447
448 let flag_span = Span::new(arg_span.start + 2, arg_span.len - 2);
449 match split(flag, flag_span) {
450 Some(tokens) => {
451 let mut tokens = tokens.into_iter();
452 let key = tokens.next().unwrap();
453 let value = tokens.next().unwrap();
454
455 let snek = key.s.to_snake_case();
456 tracing::trace!(
457 "Looking up long flag {flag} in variant (field name: {snek})"
458 );
459 let Some(field_index) = fields.iter().position(|f| f.name == snek)
460 else {
461 return Err(ArgsErrorKind::UnknownLongFlag {
462 flag: flag.to_string(),
463 fields,
464 });
465 };
466 p = self.handle_field(p, field_index, Some(value))?;
467 }
468 None => {
469 let snek = flag.to_snake_case();
470 tracing::trace!(
471 "Looking up long flag {flag} in variant (field name: {snek})"
472 );
473 let Some(field_index) = fields.iter().position(|f| f.name == snek)
474 else {
475 return Err(ArgsErrorKind::UnknownLongFlag {
476 flag: flag.to_string(),
477 fields,
478 });
479 };
480 p = self.handle_field(p, field_index, None)?;
481 }
482 }
483 }
484 ArgType::ShortFlag(flag) => {
485 let flag_span = Span::new(arg_span.start + 1, arg_span.len - 1);
486 match split(flag, flag_span) {
487 Some(tokens) => {
488 let mut tokens = tokens.into_iter();
489 let key = tokens.next().unwrap();
490 let value = tokens.next().unwrap();
491
492 let short_char = key.s;
493 tracing::trace!("Looking up short flag {short_char} in variant");
494 let Some(field_index) =
495 find_field_index_with_short_char(fields, short_char)
496 else {
497 return Err(ArgsErrorKind::UnknownShortFlag {
498 flag: short_char.to_string(),
499 fields,
500 precise_span: None, });
502 };
503 p = self.handle_field(p, field_index, Some(value))?;
504 }
505 None => {
506 p = self.process_short_flag(p, flag, flag_span, fields)?;
508 }
509 }
510 }
511 ArgType::Positional => {
512 if let Some((field_index, field)) = find_subcommand_field(fields)
514 && !p.is_field_set(field_index)?
515 {
516 p = self.handle_subcommand_field(p, field_index, field)?;
517 continue;
518 }
519
520 let mut chosen_field_index: Option<usize> = None;
522
523 for (field_index, field) in fields.iter().enumerate() {
524 let is_positional = field.has_attr(Some("args"), "positional");
525 if !is_positional {
526 continue;
527 }
528
529 if matches!(field.shape().def, Def::List(_)) {
530 } else if p.is_field_set(field_index)? {
532 continue;
533 }
534
535 chosen_field_index = Some(field_index);
536 break;
537 }
538
539 let Some(chosen_field_index) = chosen_field_index else {
540 return Err(ArgsErrorKind::UnexpectedPositionalArgument { fields });
541 };
542
543 p = p.begin_nth_field(chosen_field_index)?;
544 let value = self.args[self.index];
545
546 if let Type::User(UserType::Enum(_)) = fields[chosen_field_index].shape().ty
548 && !fields[chosen_field_index].has_attr(Some("args"), "subcommand")
549 {
550 return Err(ArgsErrorKind::EnumWithoutSubcommandAttribute {
551 field: &fields[chosen_field_index],
552 });
553 }
554
555 p = self.handle_value(p, value)?;
556 p = p.end()?;
557 self.index += 1;
558 }
559 ArgType::None => todo!(),
560 }
561 }
562
563 p = self.finalize_variant_fields(p, fields)?;
565
566 Ok(p)
567 }
568
569 fn handle_subcommand_field(
571 &mut self,
572 p: Partial<'static>,
573 field_index: usize,
574 field: &'static Field,
575 ) -> Result<Partial<'static>, ArgsErrorKind> {
576 let field_shape = field.shape();
577 tracing::trace!(
578 "Handling subcommand field: {} with shape {}",
579 field.name,
580 field_shape
581 );
582
583 let mut p = p.begin_nth_field(field_index)?;
584
585 let (is_optional, _enum_shape, enum_type) = if let Def::Option(option_def) = field_shape.def
588 {
589 let inner_shape = option_def.t;
591 if let Type::User(UserType::Enum(enum_type)) = inner_shape.ty {
592 (true, inner_shape, enum_type)
593 } else {
594 return Err(ArgsErrorKind::NoFields { shape: field_shape });
595 }
596 } else if let Type::User(UserType::Enum(enum_type)) = field_shape.ty {
597 (false, field_shape, enum_type)
599 } else {
600 return Err(ArgsErrorKind::NoFields { shape: field_shape });
601 };
602
603 let subcommand_name = self.args[self.index];
605 tracing::trace!("Looking for subcommand variant: {subcommand_name}");
606
607 let variant = match find_variant_by_name(enum_type, subcommand_name) {
609 Ok(v) => v,
610 Err(e) => {
611 if is_optional {
612 return Err(e);
617 } else {
618 return Err(e);
619 }
620 }
621 };
622
623 self.index += 1;
624
625 if is_optional {
626 p = p.begin_some()?;
628 }
629
630 p = p.select_variant_named(variant.name)?;
632
633 p = self.parse_variant_fields(p, variant)?;
635
636 if is_optional {
637 p = p.end()?; }
639
640 p = p.end()?; Ok(p)
643 }
644
645 fn finalize_struct(&self, mut p: Partial<'static>) -> Result<Partial<'static>, ArgsErrorKind> {
647 let fields = self.fields(&p)?;
648 for (field_index, field) in fields.iter().enumerate() {
649 if p.is_field_set(field_index)? {
650 continue;
651 }
652
653 if field.has_attr(Some("args"), "subcommand") {
655 let field_shape = field.shape();
656 if let Def::Option(_) = field_shape.def {
657 p = p.set_nth_field_to_default(field_index)?;
660 continue;
661 } else {
662 return Err(ArgsErrorKind::MissingSubcommand {
664 variants: get_variants_from_shape(field_shape),
665 });
666 }
667 }
668
669 if field.has_default() {
670 tracing::trace!("Setting #{field_index} field to default: {field:?}");
671 p = p.set_nth_field_to_default(field_index)?;
672 } else if field.shape().is_shape(bool::SHAPE) {
673 p = p.set_nth_field(field_index, false)?;
675 } else {
676 return Err(ArgsErrorKind::MissingArgument { field });
677 }
678 }
679 Ok(p)
680 }
681
682 fn finalize_variant_fields(
684 &self,
685 mut p: Partial<'static>,
686 fields: &'static [Field],
687 ) -> Result<Partial<'static>, ArgsErrorKind> {
688 for (field_index, field) in fields.iter().enumerate() {
689 if p.is_field_set(field_index)? {
690 continue;
691 }
692
693 if field.has_attr(Some("args"), "subcommand") {
695 let field_shape = field.shape();
696 if let Def::Option(_) = field_shape.def {
697 p = p.set_nth_field_to_default(field_index)?;
699 continue;
700 } else {
701 return Err(ArgsErrorKind::MissingSubcommand {
703 variants: get_variants_from_shape(field_shape),
704 });
705 }
706 }
707
708 if field.has_default() {
709 tracing::trace!("Setting variant field #{field_index} to default: {field:?}");
710 p = p.set_nth_field_to_default(field_index)?;
711 } else if field.shape().is_shape(bool::SHAPE) {
712 p = p.set_nth_field(field_index, false)?;
713 } else {
714 return Err(ArgsErrorKind::MissingArgument { field });
715 }
716 }
717 Ok(p)
718 }
719}
720
721fn find_variant_by_name(
723 enum_type: EnumType,
724 name: &str,
725) -> Result<&'static Variant, ArgsErrorKind> {
726 tracing::trace!(
727 "find_variant_by_name: looking for '{}' among variants: {:?}",
728 name,
729 enum_type
730 .variants
731 .iter()
732 .map(|v| v.name)
733 .collect::<Vec<_>>()
734 );
735
736 for variant in enum_type.variants {
738 if let Some(attr) = variant.get_builtin_attr("rename")
739 && let Some(rename) = attr.get_as::<&str>()
740 && *rename == name
741 {
742 return Ok(variant);
743 }
744 }
745
746 for variant in enum_type.variants {
748 let kebab_name = variant.name.to_kebab_case();
749 tracing::trace!(
750 " checking variant '{}' -> kebab '{}' against '{}'",
751 variant.name,
752 kebab_name,
753 name
754 );
755 if kebab_name == name {
756 return Ok(variant);
757 }
758 }
759
760 for variant in enum_type.variants {
762 if variant.name == name {
763 return Ok(variant);
764 }
765 }
766
767 Err(ArgsErrorKind::UnknownSubcommand {
768 provided: name.to_string(),
769 variants: enum_type.variants,
770 })
771}
772
773fn find_subcommand_field(fields: &'static [Field]) -> Option<(usize, &'static Field)> {
775 fields
776 .iter()
777 .enumerate()
778 .find(|(_, f)| f.has_attr(Some("args"), "subcommand"))
779}
780
781#[derive(Debug, PartialEq)]
783struct SplitToken<'input> {
784 s: &'input str,
785 span: Span,
786}
787
788fn split<'input>(input: &'input str, span: Span) -> Option<Vec<SplitToken<'input>>> {
792 let equals_index = input.find('=')?;
793
794 let l = &input[0..equals_index];
795 let l_span = Span::new(span.start, l.len());
796
797 let r = &input[equals_index + 1..];
798 let r_span = Span::new(equals_index + 1, r.len());
799
800 Some(vec![
801 SplitToken { s: l, span: l_span },
802 SplitToken { s: r, span: r_span },
803 ])
804}
805
806#[test]
807fn test_split() {
808 assert_eq!(split("ababa", Span::new(5, 5)), None);
809 assert_eq!(
810 split("foo=bar", Span::new(0, 7)),
811 Some(vec![
812 SplitToken {
813 s: "foo",
814 span: Span::new(0, 3)
815 },
816 SplitToken {
817 s: "bar",
818 span: Span::new(4, 3)
819 },
820 ])
821 );
822 assert_eq!(
823 split("foo=", Span::new(0, 4)),
824 Some(vec![
825 SplitToken {
826 s: "foo",
827 span: Span::new(0, 3)
828 },
829 SplitToken {
830 s: "",
831 span: Span::new(4, 0)
832 },
833 ])
834 );
835 assert_eq!(
836 split("=bar", Span::new(0, 4)),
837 Some(vec![
838 SplitToken {
839 s: "",
840 span: Span::new(0, 0)
841 },
842 SplitToken {
843 s: "bar",
844 span: Span::new(1, 3)
845 },
846 ])
847 );
848}
849
850impl<'input> Context<'input> {
851 fn process_short_flag(
861 &mut self,
862 mut p: Partial<'static>,
863 flag: &'input str,
864 flag_span: Span,
865 fields: &'static [Field],
866 ) -> Result<Partial<'static>, ArgsErrorKind> {
867 let first_char = flag.chars().next().unwrap();
869 let first_char_str = &flag[..first_char.len_utf8()];
870 let rest = &flag[first_char.len_utf8()..];
871
872 tracing::trace!("Looking up short flag '{first_char}' (rest: '{rest}')");
873
874 let Some(field_index) = find_field_index_with_short_char(fields, first_char_str) else {
876 let char_span = Span::new(flag_span.start, first_char.len_utf8());
878 return Err(ArgsErrorKind::UnknownShortFlag {
879 flag: first_char_str.to_string(),
880 fields,
881 precise_span: Some(char_span),
882 });
883 };
884
885 let field = &fields[field_index];
886 let field_shape = field.shape();
887
888 let is_bool = field_shape.is_shape(bool::SHAPE);
890 let is_bool_list = if let facet_core::Def::List(list_def) = field_shape.def {
891 list_def.t.is_shape(bool::SHAPE)
892 } else {
893 false
894 };
895
896 if rest.is_empty() {
897 if is_bool || is_bool_list {
899 p = p.begin_nth_field(field_index)?;
901
902 if is_bool_list {
903 p = p.begin_list()?;
905 p = p.begin_list_item()?;
906 p = p.set(true)?;
907 p = p.end()?; } else {
909 p = p.set(true)?;
911 }
912
913 p = p.end()?; self.index += 1; } else {
916 p = self.handle_field(p, field_index, None)?;
918 }
919 } else if is_bool || is_bool_list {
920 p = p.begin_nth_field(field_index)?;
924
925 if is_bool_list {
926 p = p.begin_list()?;
928 p = p.begin_list_item()?;
929 p = p.set(true)?;
930 p = p.end()?; } else {
932 p = p.set(true)?;
934 }
935
936 p = p.end()?; let rest_span = Span::new(flag_span.start + first_char.len_utf8(), rest.len());
940 p = self.process_short_flag(p, rest, rest_span, fields)?;
941 } else {
943 let value_span = Span::new(flag_span.start + first_char.len_utf8(), rest.len());
945 p = self.handle_field(
946 p,
947 field_index,
948 Some(SplitToken {
949 s: rest,
950 span: value_span,
951 }),
952 )?;
953 }
954
955 Ok(p)
956 }
957}
958
959fn find_field_index_with_short_char(fields: &'static [Field], short: &str) -> Option<usize> {
963 let short_char = short.chars().next()?;
964 fields.iter().position(|f| {
965 if let Some(ext) = f.get_attr(Some("args"), "short") {
966 if let Some(crate::Attr::Short(opt_char)) = ext.get_as::<crate::Attr>() {
968 match opt_char {
969 Some(c) => *c == short_char,
970 None => {
971 f.name.starts_with(short_char)
973 }
974 }
975 } else {
976 false
977 }
978 } else {
979 false
980 }
981 })
982}