Add argument choices

This commit is contained in:
Paul Ouellette
2019-05-27 17:54:05 -04:00
parent d08b637d54
commit dcd5162710
6 changed files with 64 additions and 0 deletions

View File

@@ -68,3 +68,24 @@ If more than one argument can be consumed, a table is used to store the data.
pair = {"foo", "bar"},
optional = "baz"
}
Setting argument choices
------------------------
The ``choices`` property can be used to restrict an argument to a set of choices. Its value is an array of string choices.
.. code-block:: lua
:linenos:
parser:argument "direction"
:choices {"north", "south", "east", "west"}
.. code-block:: none
$ lua script.lua foo
.. code-block:: none
Usage: script.lua [-h] <direction>
Error: argument 'direction' must be one of 'north', 'south', 'east', 'west'

View File

@@ -242,6 +242,7 @@ Property Type
``defmode`` String
``show_default`` Boolean
``argname`` String or table
``choices`` Table
``action`` Function or string
``init`` Any
``hidden`` Boolean
@@ -273,6 +274,7 @@ Property Type
``show_default`` Boolean
``overwrite`` Booleans
``argname`` String or table
``choices`` Table
``action`` Function or string
``init`` Any
``hidden`` Boolean

View File

@@ -107,6 +107,11 @@ Just as arguments, options can be configured to take several command line argume
Note that the data passed to ``optional`` option is stored in an array. That is necessary to distinguish whether the option was invoked without an argument or it was not invoked at all.
Setting argument choices
------------------------
The ``choices`` property can be used to specify a list of choices for an option argument in the same way as for arguments.
Setting number of invocations
-----------------------------

View File

@@ -161,5 +161,15 @@ describe("tests related to positional arguments", function()
}
assert.has_error(function() parser:parse{} end, "missing argument 'foo1'")
end)
it("handles invalid argument choices correctly", function()
local parse = Parser()
parse:argument "foo" {
choices = {"bar", "baz", "qu"}
}
assert.has_error(function()
parse:parse{"foo", "quu"}
end, "argument 'foo' must be one of 'bar', 'baz', 'qu'")
end)
end)
end)

View File

@@ -290,6 +290,16 @@ describe("tests related to options", function()
end, "too many arguments")
end)
it("handles invalid argument choices correctly", function()
local parse = Parser()
parse:option "-s" "--server" {
choices = {"foo", "bar", "baz"}
}
assert.has_error(function()
parse:parse{"-slocalhost"}
end, "argument for option '-s' must be one of 'foo', 'bar', 'baz'")
end)
it("doesn't accept GNU-like long options when it doesn't need arguments", function()
local parser = Parser()
parser:flag "-q" "--quiet"

View File

@@ -341,6 +341,7 @@ local Argument = class({
typechecked("defmode", "string"),
typechecked("show_default", "boolean"),
typechecked("argname", "string", "table"),
typechecked("choices", "table"),
typechecked("hidden", "boolean"),
option_action,
option_init
@@ -363,6 +364,7 @@ local Option = class({
typechecked("show_default", "boolean"),
typechecked("overwrite", "boolean"),
typechecked("argname", "string", "table"),
typechecked("choices", "table"),
typechecked("hidden", "boolean"),
option_action,
option_init
@@ -1204,7 +1206,21 @@ function ElementState:invoke()
return self.open
end
function ElementState:check_choices(argument)
if self.element._choices then
for _, choice in ipairs(self.element._choices) do
if argument == choice then
return
end
end
local choices_list = "'" .. table.concat(self.element._choices, "', '") .. "'"
local is_option = getmetatable(self.element) == Option
self:error("%s%s must be one of %s", is_option and "argument for " or "", self.name, choices_list)
end
end
function ElementState:pass(argument)
self:check_choices(argument)
argument = self:convert(argument, #self.args + 1)
table.insert(self.args, argument)