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, StructKind, 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 if variant.data.kind == StructKind::TupleStruct && fields.len() == 1 {
428 let inner_shape = fields[0].shape();
429 if let Type::User(UserType::Struct(struct_type)) = inner_shape.ty {
430 p = p.begin_nth_field(0)?;
433 p = self.parse_fields_loop(p, struct_type.fields)?;
436 p = self.finalize_variant_fields(p, struct_type.fields)?;
437 p = p.end()?;
438 return Ok(p);
439 }
440 }
441
442 while self.args.len() > self.index {
443 let arg = self.args[self.index];
444 let arg_span = Span::new(self.arg_indices[self.index], arg.len());
445 let at = if self.positional_only {
446 ArgType::Positional
447 } else {
448 ArgType::parse(arg)
449 };
450 tracing::trace!("Parsing variant field, arg: {at:?}");
451
452 match at {
453 ArgType::DoubleDash => {
454 self.positional_only = true;
455 self.index += 1;
456 }
457 ArgType::LongFlag(flag) => {
458 if flag.starts_with('-') {
460 return Err(ArgsErrorKind::UnknownLongFlag {
461 flag: flag.to_string(),
462 fields,
463 });
464 }
465
466 let flag_span = Span::new(arg_span.start + 2, arg_span.len - 2);
467 match split(flag, flag_span) {
468 Some(tokens) => {
469 let mut tokens = tokens.into_iter();
470 let key = tokens.next().unwrap();
471 let value = tokens.next().unwrap();
472
473 let snek = key.s.to_snake_case();
474 tracing::trace!(
475 "Looking up long flag {flag} in variant (field name: {snek})"
476 );
477 let Some(field_index) = fields.iter().position(|f| f.name == snek)
478 else {
479 return Err(ArgsErrorKind::UnknownLongFlag {
480 flag: flag.to_string(),
481 fields,
482 });
483 };
484 p = self.handle_field(p, field_index, Some(value))?;
485 }
486 None => {
487 let snek = flag.to_snake_case();
488 tracing::trace!(
489 "Looking up long flag {flag} in variant (field name: {snek})"
490 );
491 let Some(field_index) = fields.iter().position(|f| f.name == snek)
492 else {
493 return Err(ArgsErrorKind::UnknownLongFlag {
494 flag: flag.to_string(),
495 fields,
496 });
497 };
498 p = self.handle_field(p, field_index, None)?;
499 }
500 }
501 }
502 ArgType::ShortFlag(flag) => {
503 let flag_span = Span::new(arg_span.start + 1, arg_span.len - 1);
504 match split(flag, flag_span) {
505 Some(tokens) => {
506 let mut tokens = tokens.into_iter();
507 let key = tokens.next().unwrap();
508 let value = tokens.next().unwrap();
509
510 let short_char = key.s;
511 tracing::trace!("Looking up short flag {short_char} in variant");
512 let Some(field_index) =
513 find_field_index_with_short_char(fields, short_char)
514 else {
515 return Err(ArgsErrorKind::UnknownShortFlag {
516 flag: short_char.to_string(),
517 fields,
518 precise_span: None, });
520 };
521 p = self.handle_field(p, field_index, Some(value))?;
522 }
523 None => {
524 p = self.process_short_flag(p, flag, flag_span, fields)?;
526 }
527 }
528 }
529 ArgType::Positional => {
530 if let Some((field_index, field)) = find_subcommand_field(fields)
532 && !p.is_field_set(field_index)?
533 {
534 p = self.handle_subcommand_field(p, field_index, field)?;
535 continue;
536 }
537
538 let mut chosen_field_index: Option<usize> = None;
540
541 for (field_index, field) in fields.iter().enumerate() {
542 let is_positional = field.has_attr(Some("args"), "positional");
543 if !is_positional {
544 continue;
545 }
546
547 if matches!(field.shape().def, Def::List(_)) {
548 } else if p.is_field_set(field_index)? {
550 continue;
551 }
552
553 chosen_field_index = Some(field_index);
554 break;
555 }
556
557 let Some(chosen_field_index) = chosen_field_index else {
558 return Err(ArgsErrorKind::UnexpectedPositionalArgument { fields });
559 };
560
561 p = p.begin_nth_field(chosen_field_index)?;
562 let value = self.args[self.index];
563
564 if let Type::User(UserType::Enum(_)) = fields[chosen_field_index].shape().ty
566 && !fields[chosen_field_index].has_attr(Some("args"), "subcommand")
567 {
568 return Err(ArgsErrorKind::EnumWithoutSubcommandAttribute {
569 field: &fields[chosen_field_index],
570 });
571 }
572
573 p = self.handle_value(p, value)?;
574 p = p.end()?;
575 self.index += 1;
576 }
577 ArgType::None => todo!(),
578 }
579 }
580
581 p = self.finalize_variant_fields(p, fields)?;
583
584 Ok(p)
585 }
586
587 fn handle_subcommand_field(
589 &mut self,
590 p: Partial<'static>,
591 field_index: usize,
592 field: &'static Field,
593 ) -> Result<Partial<'static>, ArgsErrorKind> {
594 let field_shape = field.shape();
595 tracing::trace!(
596 "Handling subcommand field: {} with shape {}",
597 field.name,
598 field_shape
599 );
600
601 let mut p = p.begin_nth_field(field_index)?;
602
603 let (is_optional, _enum_shape, enum_type) = if let Def::Option(option_def) = field_shape.def
606 {
607 let inner_shape = option_def.t;
609 if let Type::User(UserType::Enum(enum_type)) = inner_shape.ty {
610 (true, inner_shape, enum_type)
611 } else {
612 return Err(ArgsErrorKind::NoFields { shape: field_shape });
613 }
614 } else if let Type::User(UserType::Enum(enum_type)) = field_shape.ty {
615 (false, field_shape, enum_type)
617 } else {
618 return Err(ArgsErrorKind::NoFields { shape: field_shape });
619 };
620
621 let subcommand_name = self.args[self.index];
623 tracing::trace!("Looking for subcommand variant: {subcommand_name}");
624
625 let variant = match find_variant_by_name(enum_type, subcommand_name) {
627 Ok(v) => v,
628 Err(e) => {
629 if is_optional {
630 return Err(e);
635 } else {
636 return Err(e);
637 }
638 }
639 };
640
641 self.index += 1;
642
643 if self.index < self.args.len() && is_help_flag(self.args[self.index]) {
645 let help_text = crate::help::generate_subcommand_help(
647 variant,
648 "command", &HelpConfig::default(),
650 );
651 return Err(ArgsErrorKind::HelpRequested { help_text });
652 }
653
654 if is_optional {
655 p = p.begin_some()?;
657 }
658
659 p = p.select_variant_named(variant.name)?;
661
662 p = self.parse_variant_fields(p, variant)?;
664
665 if is_optional {
666 p = p.end()?; }
668
669 p = p.end()?; Ok(p)
672 }
673
674 fn parse_fields_loop(
676 &mut self,
677 mut p: Partial<'static>,
678 fields: &'static [Field],
679 ) -> Result<Partial<'static>, ArgsErrorKind> {
680 while self.args.len() > self.index {
681 let arg = self.args[self.index];
682 let arg_span = Span::new(self.arg_indices[self.index], arg.len());
683 let at = if self.positional_only {
684 ArgType::Positional
685 } else {
686 ArgType::parse(arg)
687 };
688 tracing::trace!("Parsing flattened struct field, arg: {at:?}");
689
690 match at {
691 ArgType::DoubleDash => {
692 self.positional_only = true;
693 self.index += 1;
694 }
695 ArgType::LongFlag(flag) => {
696 if flag.starts_with('-') {
697 return Err(ArgsErrorKind::UnknownLongFlag {
698 flag: flag.to_string(),
699 fields,
700 });
701 }
702
703 let flag_span = Span::new(arg_span.start + 2, arg_span.len - 2);
704 match split(flag, flag_span) {
705 Some(tokens) => {
706 let mut tokens = tokens.into_iter();
707 let key = tokens.next().unwrap();
708 let value = tokens.next().unwrap();
709
710 let snek = key.s.to_snake_case();
711 let Some(field_index) = fields.iter().position(|f| f.name == snek)
712 else {
713 return Err(ArgsErrorKind::UnknownLongFlag {
714 flag: flag.to_string(),
715 fields,
716 });
717 };
718 p = self.handle_field(p, field_index, Some(value))?;
719 }
720 None => {
721 let snek = flag.to_snake_case();
722 let Some(field_index) = fields.iter().position(|f| f.name == snek)
723 else {
724 return Err(ArgsErrorKind::UnknownLongFlag {
725 flag: flag.to_string(),
726 fields,
727 });
728 };
729 p = self.handle_field(p, field_index, None)?;
730 }
731 }
732 }
733 ArgType::ShortFlag(flag) => {
734 let flag_span = Span::new(arg_span.start + 1, arg_span.len - 1);
735 match split(flag, flag_span) {
736 Some(tokens) => {
737 let mut tokens = tokens.into_iter();
738 let key = tokens.next().unwrap();
739 let value = tokens.next().unwrap();
740
741 let short_char = key.s;
742 let Some(field_index) =
743 find_field_index_with_short_char(fields, short_char)
744 else {
745 return Err(ArgsErrorKind::UnknownShortFlag {
746 flag: short_char.to_string(),
747 fields,
748 precise_span: None,
749 });
750 };
751 p = self.handle_field(p, field_index, Some(value))?;
752 }
753 None => {
754 p = self.process_short_flag(p, flag, flag_span, fields)?;
755 }
756 }
757 }
758 ArgType::Positional => {
759 let mut chosen_field_index: Option<usize> = None;
761 for (field_index, field) in fields.iter().enumerate() {
762 let is_positional = field.has_attr(Some("args"), "positional");
763 if !is_positional {
764 continue;
765 }
766
767 if matches!(field.shape().def, Def::List(_)) {
770 } else if p.is_field_set(field_index)? {
772 continue;
773 }
774
775 chosen_field_index = Some(field_index);
776 break;
777 }
778
779 if let Some(field_index) = chosen_field_index {
780 let value = SplitToken {
781 s: arg,
782 span: arg_span,
783 };
784 p = self.handle_field(p, field_index, Some(value))?;
785 } else {
786 return Err(ArgsErrorKind::UnexpectedPositionalArgument { fields });
787 }
788 }
789 ArgType::None => todo!(),
790 }
791 }
792 Ok(p)
793 }
794
795 fn finalize_struct(&self, mut p: Partial<'static>) -> Result<Partial<'static>, ArgsErrorKind> {
797 let fields = self.fields(&p)?;
798 for (field_index, field) in fields.iter().enumerate() {
799 if p.is_field_set(field_index)? {
800 continue;
801 }
802
803 if field.has_attr(Some("args"), "subcommand") {
805 let field_shape = field.shape();
806 if let Def::Option(_) = field_shape.def {
807 p = p.set_nth_field_to_default(field_index)?;
810 continue;
811 } else {
812 return Err(ArgsErrorKind::MissingSubcommand {
814 variants: get_variants_from_shape(field_shape),
815 });
816 }
817 }
818
819 if field.has_default() {
820 tracing::trace!("Setting #{field_index} field to default: {field:?}");
821 p = p.set_nth_field_to_default(field_index)?;
822 } else if field.shape().is_shape(bool::SHAPE) {
823 p = p.set_nth_field(field_index, false)?;
825 } else if let Def::Option(_) = field.shape().def {
826 p = p.set_nth_field_to_default(field_index)?;
828 } else {
829 return Err(ArgsErrorKind::MissingArgument { field });
830 }
831 }
832 Ok(p)
833 }
834
835 fn finalize_variant_fields(
837 &self,
838 mut p: Partial<'static>,
839 fields: &'static [Field],
840 ) -> Result<Partial<'static>, ArgsErrorKind> {
841 for (field_index, field) in fields.iter().enumerate() {
842 if p.is_field_set(field_index)? {
843 continue;
844 }
845
846 if field.has_attr(Some("args"), "subcommand") {
848 let field_shape = field.shape();
849 if let Def::Option(_) = field_shape.def {
850 p = p.set_nth_field_to_default(field_index)?;
852 continue;
853 } else {
854 return Err(ArgsErrorKind::MissingSubcommand {
856 variants: get_variants_from_shape(field_shape),
857 });
858 }
859 }
860
861 if field.has_default() {
862 tracing::trace!("Setting variant field #{field_index} to default: {field:?}");
863 p = p.set_nth_field_to_default(field_index)?;
864 } else if field.shape().is_shape(bool::SHAPE) {
865 p = p.set_nth_field(field_index, false)?;
866 } else if let Def::Option(_) = field.shape().def {
867 p = p.set_nth_field_to_default(field_index)?;
869 } else {
870 return Err(ArgsErrorKind::MissingArgument { field });
871 }
872 }
873 Ok(p)
874 }
875}
876
877fn find_variant_by_name(
879 enum_type: EnumType,
880 name: &str,
881) -> Result<&'static Variant, ArgsErrorKind> {
882 tracing::trace!(
883 "find_variant_by_name: looking for '{}' among variants: {:?}",
884 name,
885 enum_type
886 .variants
887 .iter()
888 .map(|v| v.name)
889 .collect::<Vec<_>>()
890 );
891
892 for variant in enum_type.variants {
894 if let Some(attr) = variant.get_builtin_attr("rename")
895 && let Some(rename) = attr.get_as::<&str>()
896 && *rename == name
897 {
898 return Ok(variant);
899 }
900 }
901
902 for variant in enum_type.variants {
904 let kebab_name = variant.name.to_kebab_case();
905 tracing::trace!(
906 " checking variant '{}' -> kebab '{}' against '{}'",
907 variant.name,
908 kebab_name,
909 name
910 );
911 if kebab_name == name {
912 return Ok(variant);
913 }
914 }
915
916 for variant in enum_type.variants {
918 if variant.name == name {
919 return Ok(variant);
920 }
921 }
922
923 Err(ArgsErrorKind::UnknownSubcommand {
924 provided: name.to_string(),
925 variants: enum_type.variants,
926 })
927}
928
929fn find_subcommand_field(fields: &'static [Field]) -> Option<(usize, &'static Field)> {
931 fields
932 .iter()
933 .enumerate()
934 .find(|(_, f)| f.has_attr(Some("args"), "subcommand"))
935}
936
937#[derive(Debug, PartialEq)]
939struct SplitToken<'input> {
940 s: &'input str,
941 span: Span,
942}
943
944fn split<'input>(input: &'input str, span: Span) -> Option<Vec<SplitToken<'input>>> {
948 let equals_index = input.find('=')?;
949
950 let l = &input[0..equals_index];
951 let l_span = Span::new(span.start, l.len());
952
953 let r = &input[equals_index + 1..];
954 let r_span = Span::new(equals_index + 1, r.len());
955
956 Some(vec![
957 SplitToken { s: l, span: l_span },
958 SplitToken { s: r, span: r_span },
959 ])
960}
961
962#[test]
963fn test_split() {
964 assert_eq!(split("ababa", Span::new(5, 5)), None);
965 assert_eq!(
966 split("foo=bar", Span::new(0, 7)),
967 Some(vec![
968 SplitToken {
969 s: "foo",
970 span: Span::new(0, 3)
971 },
972 SplitToken {
973 s: "bar",
974 span: Span::new(4, 3)
975 },
976 ])
977 );
978 assert_eq!(
979 split("foo=", Span::new(0, 4)),
980 Some(vec![
981 SplitToken {
982 s: "foo",
983 span: Span::new(0, 3)
984 },
985 SplitToken {
986 s: "",
987 span: Span::new(4, 0)
988 },
989 ])
990 );
991 assert_eq!(
992 split("=bar", Span::new(0, 4)),
993 Some(vec![
994 SplitToken {
995 s: "",
996 span: Span::new(0, 0)
997 },
998 SplitToken {
999 s: "bar",
1000 span: Span::new(1, 3)
1001 },
1002 ])
1003 );
1004}
1005
1006impl<'input> Context<'input> {
1007 fn process_short_flag(
1017 &mut self,
1018 mut p: Partial<'static>,
1019 flag: &'input str,
1020 flag_span: Span,
1021 fields: &'static [Field],
1022 ) -> Result<Partial<'static>, ArgsErrorKind> {
1023 let first_char = flag.chars().next().unwrap();
1025 let first_char_str = &flag[..first_char.len_utf8()];
1026 let rest = &flag[first_char.len_utf8()..];
1027
1028 tracing::trace!("Looking up short flag '{first_char}' (rest: '{rest}')");
1029
1030 let Some(field_index) = find_field_index_with_short_char(fields, first_char_str) else {
1032 let char_span = Span::new(flag_span.start, first_char.len_utf8());
1034 return Err(ArgsErrorKind::UnknownShortFlag {
1035 flag: first_char_str.to_string(),
1036 fields,
1037 precise_span: Some(char_span),
1038 });
1039 };
1040
1041 let field = &fields[field_index];
1042 let field_shape = field.shape();
1043
1044 let is_bool = field_shape.is_shape(bool::SHAPE);
1046 let is_bool_list = if let facet_core::Def::List(list_def) = field_shape.def {
1047 list_def.t.is_shape(bool::SHAPE)
1048 } else {
1049 false
1050 };
1051
1052 if rest.is_empty() {
1053 if is_bool || is_bool_list {
1055 p = p.begin_nth_field(field_index)?;
1057
1058 if is_bool_list {
1059 p = p.begin_list()?;
1061 p = p.begin_list_item()?;
1062 p = p.set(true)?;
1063 p = p.end()?; } else {
1065 p = p.set(true)?;
1067 }
1068
1069 p = p.end()?; self.index += 1; } else {
1072 p = self.handle_field(p, field_index, None)?;
1074 }
1075 } else if is_bool || is_bool_list {
1076 p = p.begin_nth_field(field_index)?;
1080
1081 if is_bool_list {
1082 p = p.begin_list()?;
1084 p = p.begin_list_item()?;
1085 p = p.set(true)?;
1086 p = p.end()?; } else {
1088 p = p.set(true)?;
1090 }
1091
1092 p = p.end()?; let rest_span = Span::new(flag_span.start + first_char.len_utf8(), rest.len());
1096 p = self.process_short_flag(p, rest, rest_span, fields)?;
1097 } else {
1099 let value_span = Span::new(flag_span.start + first_char.len_utf8(), rest.len());
1101 p = self.handle_field(
1102 p,
1103 field_index,
1104 Some(SplitToken {
1105 s: rest,
1106 span: value_span,
1107 }),
1108 )?;
1109 }
1110
1111 Ok(p)
1112 }
1113}
1114
1115fn find_field_index_with_short_char(fields: &'static [Field], short: &str) -> Option<usize> {
1119 let short_char = short.chars().next()?;
1120 fields.iter().position(|f| {
1121 if let Some(ext) = f.get_attr(Some("args"), "short") {
1122 if let Some(crate::Attr::Short(opt_char)) = ext.get_as::<crate::Attr>() {
1124 match opt_char {
1125 Some(c) => *c == short_char,
1126 None => {
1127 f.name.starts_with(short_char)
1129 }
1130 }
1131 } else {
1132 false
1133 }
1134 } else {
1135 false
1136 }
1137 })
1138}