Skip to content

Commit 5eec033

Browse files
Refactor OptionParser#parse (#16233)
Extracts logic into a couple of helper methods. This should not alter behavior in any way. Preparatory step toward enabling short option combinations (#10981). Co-authored-by: Johannes Müller <[email protected]>
1 parent 1d83966 commit 5eec033

File tree

1 file changed

+91
-74
lines changed

1 file changed

+91
-74
lines changed

src/option_parser.cr

Lines changed: 91 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -401,67 +401,9 @@ class OptionParser
401401
break
402402
end
403403

404-
if arg.starts_with?("--")
405-
value_index = arg.index('=')
406-
if value_index
407-
flag = arg[0...value_index]
408-
value = arg[value_index + 1..-1]
409-
else
410-
flag = arg
411-
value = nil
412-
end
413-
elsif arg.starts_with?('-')
414-
if arg.size > 2
415-
flag = arg[0..1]
416-
value = arg[2..-1]
417-
else
418-
flag = arg
419-
value = nil
420-
end
421-
else
422-
flag = arg
423-
value = nil
424-
end
425-
426-
# Fetch handler of the flag.
427-
# If value is given even though handler does not take value, it is invalid, then it is skipped.
428-
if (handler = @handlers[flag]?) && !(handler.value_type.none? && value)
429-
handled_args << arg_index
430-
431-
if !value
432-
case handler.value_type
433-
in FlagValue::Required
434-
value = args[arg_index + 1]?
435-
if value
436-
handled_args << arg_index + 1
437-
arg_index += 1
438-
else
439-
@missing_option.call(flag)
440-
end
441-
in FlagValue::Optional
442-
unless gnu_optional_args?
443-
value = args[arg_index + 1]?
444-
if value && !@handlers.has_key?(value)
445-
handled_args << arg_index + 1
446-
arg_index += 1
447-
else
448-
value = nil
449-
end
450-
end
451-
in FlagValue::None
452-
# do nothing
453-
end
454-
end
404+
flag, value = parse_arg_to_flag_and_value(arg)
455405

456-
# If this is a subcommand (flag not starting with -), delete all
457-
# subcommands since they are no longer valid.
458-
unless flag.starts_with?('-')
459-
@handlers.select! { |k, _| k.starts_with?('-') }
460-
@flags.select!(&.starts_with?(" -"))
461-
end
462-
463-
handler.block.call(value || "")
464-
end
406+
arg_index = handle_flag(flag, value, arg_index, args, handled_args)
465407

466408
arg_index += 1
467409
end
@@ -476,20 +418,7 @@ class OptionParser
476418
end
477419

478420
# After argument parsing, delete handled arguments from args.
479-
# We reverse so that we delete args from
480-
handled_args.reverse!
481-
i = 0
482-
args.reject! do
483-
# handled_args is sorted in reverse so we know that i <= handled_args.last
484-
handled = i == handled_args.last?
485-
486-
# Maintain the i <= handled_args.last invariant
487-
handled_args.pop if handled
488-
489-
i += 1
490-
491-
handled
492-
end
421+
remove_handled_args(args, handled_args)
493422

494423
# Since we've deleted all handled arguments, `args` is all unknown arguments
495424
# which we split by the index of any double dash argument
@@ -514,4 +443,92 @@ class OptionParser
514443
end
515444
end
516445
end
446+
447+
# Parses a command-line argument into a flag and optional inline value.
448+
private def parse_arg_to_flag_and_value(arg : String) : {String, String?}
449+
if arg.starts_with?("--")
450+
value_index = arg.index('=')
451+
if value_index
452+
flag = arg[0...value_index]
453+
value = arg[value_index + 1..-1]
454+
else
455+
flag = arg
456+
value = nil
457+
end
458+
elsif arg.starts_with?('-')
459+
if arg.size > 2
460+
flag = arg[0..1]
461+
value = arg[2..-1]
462+
else
463+
flag = arg
464+
value = nil
465+
end
466+
else
467+
flag = arg
468+
value = nil
469+
end
470+
{flag, value}
471+
end
472+
473+
# Processes a single flag/subcommand. Matches original behaviour exactly.
474+
private def handle_flag(flag : String, value : String?, arg_index : Int32, args : Array(String), handled_args : Array(Int32)) : Int32
475+
if (handler = @handlers[flag]?) && !(handler.value_type.none? && value)
476+
handled_args << arg_index
477+
478+
if !value
479+
case handler.value_type
480+
in FlagValue::Required
481+
value = args[arg_index + 1]?
482+
if value
483+
handled_args << arg_index + 1
484+
arg_index += 1
485+
else
486+
@missing_option.call(flag)
487+
end
488+
in FlagValue::Optional
489+
unless gnu_optional_args?
490+
value = args[arg_index + 1]?
491+
if value && !@handlers.has_key?(value)
492+
handled_args << arg_index + 1
493+
arg_index += 1
494+
else
495+
value = nil
496+
end
497+
end
498+
in FlagValue::None
499+
# do nothing
500+
end
501+
end
502+
503+
# If this is a subcommand (flag not starting with -), delete all
504+
# subcommands since they are no longer valid.
505+
unless flag.starts_with?('-')
506+
@handlers.select! { |k, _| k.starts_with?('-') }
507+
@flags.select!(&.starts_with?(" -"))
508+
end
509+
510+
handler.block.call(value || "")
511+
end
512+
513+
arg_index
514+
end
515+
516+
# Removes handled arguments from the args array based on handled_args indexes.
517+
private def remove_handled_args(args : Array(String), handled_args : Array(Int32)) : Nil
518+
# After argument parsing, delete handled arguments from args.
519+
# We reverse so that we delete args from the end
520+
handled_args.reverse!
521+
i = 0
522+
args.reject! do
523+
# handled_args is sorted in reverse so we know that i <= handled_args.last
524+
handled = i == handled_args.last?
525+
526+
# Maintain the i <= handled_args.last invariant
527+
handled_args.pop if handled
528+
529+
i += 1
530+
531+
handled
532+
end
533+
end
517534
end

0 commit comments

Comments
 (0)