Skip to content

Commit

Permalink
Add support to option for parsing list of arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrmurach committed Mar 31, 2020
1 parent 92dd193 commit d034bc9
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 5 deletions.
45 changes: 40 additions & 5 deletions lib/tty/option/parser/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,12 @@ def process_double_option(long, sep, rest)
if opt.argument_required?
if !rest.empty? || sep.to_s.include?("=")
value = rest
if opt.multi_argument? &&
!(consumed = consume_arguments(opt)).empty?
value = [rest] + consumed
end
elsif !@argv.empty?
value = @argv.shift
value = opt.multi_argument? ? consume_arguments(opt) : @argv.shift
else
record_error(MissingArgument,
"option #{long} requires an argument",
Expand All @@ -123,8 +127,12 @@ def process_double_option(long, sep, rest)
elsif opt.argument_optional?
if !rest.empty?
value = rest
if opt.multi_argument? &&
!(consumed = consume_arguments(opt)).empty?
value = [rest] + consumed
end
elsif !@argv.empty?
value = @argv.shift
value = opt.multi_argument? ? consume_arguments(opt) : @argv.shift
end
else # boolean flag
value = true
Expand Down Expand Up @@ -164,8 +172,12 @@ def process_single_option(short, other_singles)
if opt.argument_required?
if !other_singles.empty?
value = other_singles
if opt.multi_argument? &&
!(consumed = consume_arguments(opt)).empty?
value = [other_singles] + consumed
end
elsif !@argv.empty?
value = @argv.shift
value = opt.multi_argument? ? consume_arguments(opt) : @argv.shift
else
record_error(MissingArgument,
"option #{short} requires an argument",
Expand All @@ -174,8 +186,12 @@ def process_single_option(short, other_singles)
elsif opt.argument_optional?
if !other_singles.empty?
value = other_singles
if opt.multi_argument? &&
!(consumed = consume_arguments(opt)).empty?
value = [other_singles] + consumed
end
elsif !@argv.empty?
value = @argv.shift
value = opt.multi_argument? ? consume_arguments(opt) : @argv.shift
end
else # boolean flag
if !other_singles.empty?
Expand All @@ -190,6 +206,23 @@ def process_single_option(short, other_singles)
[opt, value]
end

# Consume multi argument
#
# @param [Option] opt
#
# @api private
def consume_arguments(opt, values: [])
while (value = @argv.first) && !option?(value)
val = @argv.shift
values << val
end

values.size == 1 ? values.first : values
end

# Check if values looks like option
#
# @api private
def option?(value)
!value.match(/^-./).nil?
end
Expand All @@ -214,7 +247,9 @@ def record_error(type, message, opt = nil)
# @api private
def assign_option(opt, val)
if opt.multiple?
(@parsed[opt.name] ||= []) << ParamConversion[opt, val]
Array(ParamConversion[opt, val]).each do |v|
(@parsed[opt.name] ||= []) << v
end
else
@parsed[opt.name] = ParamConversion[opt, val]
end
Expand Down
149 changes: 149 additions & 0 deletions spec/unit/parser/options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -325,4 +325,153 @@ def parse(argv, options, **config)
expect(rest).to eq([])
end
end

context "when list argument" do
it "parses short option with a list argument" do
options = []
options << option(:foo, short: "-f list", convert: :list)
options << option(:bar, short: "-b")

params, = parse(%w[-f a b c -b], options)

expect(params[:foo]).to eq(%w[a b c])
end

it "parses compacted short options with a separate list argument" do
options = []
options << option(:foo, short: "-f")
options << option(:bar, short: "-b")
options << option(:qux, short: "-q list", convert: :list)

params, = parse(%w[-fbq a b c], options)

expect(params[:foo]).to eq(true)
expect(params[:bar]).to eq(true)
expect(params[:qux]).to eq(%w[a b c])
end

it "parses compacted short options with a list argument glued together" do
options = []
options << option(:foo, short: "-f")
options << option(:bar, short: "-b")
options << option(:qux, short: "-q list", convert: :list)

params, = parse(%w[-fbqa b c], options)

expect(params[:foo]).to eq(true)
expect(params[:bar]).to eq(true)
expect(params[:qux]).to eq(%w[a b c])
end

it "parses short option with an optional list argument" do
options = []
options << option(:foo, short: "-f [list]", convert: :list)
options << option(:bar, short: "-b")

params, rest = parse(%w[-f a b c -b], options)

expect(params[:foo]).to eq(%w[a b c])
expect(params[:bar]).to eq(true)
expect(rest).to eq([])
end

it "parses short option with a list argument provided together" do
options = []
options << option(:foo, short: "-f list", convert: :list)

params, = parse(%w[-fa b c], options)

expect(params[:foo]).to eq(%w[a b c])
end

it "parses short option with an optional list argument provided together" do
options = []
options << option(:foo, short: "-f [list]", convert: :list)

params, = parse(%w[-fa b c], options)

expect(params[:foo]).to eq(%w[a b c])
end

it "parses short option with a list comma delimited argument" do
options = []
options << option(:foo, short: "-f list", convert: :list)

params, = parse(%w[-f a,b,c], options)

expect(params[:foo]).to eq(%w[a b c])
end

it "parses long option with a list argument and assigment symbol" do
options = []
options << option(:foo, long: "--foo=list", convert: :list)

params, = parse(%w[--foo=a b c], options)

expect(params[:foo]).to eq(%w[a b c])
end

it "parses long option with an optional list argument" do
options = []
options << option(:foo, long: "--foo [list]", convert: :list)

params, = parse(%w[--foo=a b c], options)

expect(params[:foo]).to eq(%w[a b c])
end

it "parses long option with list argument and cast" do
options = []
options << option(:foo, long: "--foo list", convert: :list)
options << option(:bar, long: "--bar")

params, rest = parse(%w[--foo a b c --bar], options)

expect(params[:foo]).to eq(%w[a b c])
expect(params[:bar]).to eq(true)
expect(rest).to eq([])
end

it "parses long option with optional list argument and cast" do
options = []
options << option(:foo, long: "--foo [list]", convert: :list)
options << option(:bar, long: "--bar")

params, rest = parse(%w[--foo a b c --bar], options)

expect(params[:foo]).to eq(%w[a b c])
expect(rest).to eq([])
end

it "doesn't mix with other long options" do
options = []
options << option(:foo, long: "--foo list", convert: :list)
options << option(:bar, long: "--bar list", convert: :list)

params, rest = parse(%w[--foo a b c --bar x y], options)

expect(params[:foo]).to eq(%w[a b c])
expect(params[:bar]).to eq(%w[x y])
expect(rest).to eq([])
end

it "combines multiple options with list arguments" do
options = []
options << option(:foo, short: "-f list", convert: :list, arity: :any)

params, rest = parse(%w[-f a b -f c d], options)

expect(params[:foo]).to eq(%w[a b c d])
expect(rest).to eq([])
end

it "parses option with a conversion" do
options = []
options << option(:foo, long: "--foo=list", convert: :list)

params, = parse(%w[--foo=,,], options)

expect(params[:foo]).to eq([])
end
end
end

0 comments on commit d034bc9

Please sign in to comment.