Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added OptionParser#order and #order! #4809

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions spec/std/option_parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,61 @@ describe "OptionParser" do
called.should be_true
end

it "parses in order, calling handlers" do
x = false
vv = false
data : String? = nil
parser = OptionParser.new do |opts|
opts.on("-x", "test x") do
x = true
end
opts.on("--vv", "test vv") do
vv = true
end
opts.on("--data DATA", "test with data") do |d|
data = d
end
end

args = parser.order(["-x", "--vv", "--data", "some data", "other", "rest", "--vv"])
x.should be_true
vv.should be_true
data.should eq "some data"
end

it "parses in order, returning unknown args" do
parser = OptionParser.new do |opts|
opts.on("-x", "test x") do
end
opts.on("--vv", "test vv") do
end
opts.on("--data DATA", "test with data") do |d|
end
end

args = parser.order(["-x", "--vv", "--data", "some data", "other", "rest", "--vv"])
args.should eq ["other", "rest", "--vv"]

arg = args.shift
arg.should eq "other"

args = parser.order args
args.should eq ["rest", "--vv"]

arg = args.shift
arg.should eq "rest"
args = parser.order args
args.should eq [] of String

op = OptionParser.new
[""].should eq op.order [""]
["foo bar"].should eq op.order ["foo bar"]
["- foo bar"].should eq op.order ["- foo bar"]
["foo - bar"].should eq op.order ["foo - bar"]
["foo -- bar"].should eq op.order ["foo -- bar"]
["foo -- --help bar"].should eq op.order ["foo -- --help bar"]
end

describe "multiple times" do
it "gets an existence flag multiple times" do
args = %w(-f -f -f)
Expand Down
85 changes: 78 additions & 7 deletions src/option_parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ class OptionParser
# :nodoc:
record Handler,
flag : String,
block : String ->
block : String ->,
has_value : Int32?,
prefix : String?

# Creates a new parser, with its configuration specified in the block,
# and uses it to parse the passed *args*.
Expand Down Expand Up @@ -106,7 +108,16 @@ class OptionParser
check_starts_with_dash flag, "flag"

append_flag flag, description
@handlers << Handler.new(flag, block)

has_value = /([ =].+)/
if has_arg = flag =~ has_value
argument = $1
flag += argument unless flag =~ has_value
end

prefix = flag.split(/[ =]/)[0]

@handlers << Handler.new(flag, block, has_arg, prefix)
end

# Establishes a handler for a pair of short and long flags.
Expand All @@ -118,14 +129,17 @@ class OptionParser

append_flag "#{short_flag}, #{long_flag}", description

has_argument = /([ =].+)/
if long_flag =~ has_argument
has_value = /([ =].+)/
if has_arg = long_flag =~ has_value
argument = $1
short_flag += argument unless short_flag =~ has_argument
short_flag += argument unless short_flag =~ has_value
end

@handlers << Handler.new(short_flag, block)
@handlers << Handler.new(long_flag, block)
short_prefix = short_flag.split(/[ =]/)[0]
long_prefix = long_flag.split(/[ =]/)[0]

@handlers << Handler.new(short_flag, block, has_arg, short_prefix)
@handlers << Handler.new(long_flag, block, has_arg, long_prefix)
end

# Adds a separator, with an optional header message,
Expand Down Expand Up @@ -185,6 +199,22 @@ class OptionParser
parse ARGV
end

# Parses the passed *args* in order, running the handlers associated to each option,
# until unhandle arg.
#
# Returns unhandled args for further processing.
def order(args)
OrderTask.new(self, args).parse
end

# Parses the passed *args* in order, running the handlers associated to each option,
# until unhandle arg.
#
# Returns unhandled args for further processing.
def order!
order ARGV
end

private def check_starts_with_dash(arg, name, allow_empty = false)
return if allow_empty && arg.empty?

Expand Down Expand Up @@ -330,4 +360,45 @@ class OptionParser
end
end
end

private struct OrderTask
def initialize(@parser : OptionParser, @args : Array(String))
end

private def find_handler(arg : String)
arg_prefix = arg.split(/[=]/)[0]
@parser.handlers.each do |handler|
if handler.prefix == arg_prefix
return handler
end
end
end

def parse
args = @args.dup
while arg = args.shift?
if handler = find_handler(arg)
flag = handler.flag
block = handler.block

if handler.has_value
pair = arg.split(/\=/, 2)
value = if pair.size == 2
pair[1]
else
args.shift
end
block.call value
else
block.call ""
end
else
args.unshift arg
return args
end
end

args
end
end
end