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"}, pair = {"foo", "bar"},
optional = "baz" 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 ``defmode`` String
``show_default`` Boolean ``show_default`` Boolean
``argname`` String or table ``argname`` String or table
``choices`` Table
``action`` Function or string ``action`` Function or string
``init`` Any ``init`` Any
``hidden`` Boolean ``hidden`` Boolean
@@ -273,6 +274,7 @@ Property Type
``show_default`` Boolean ``show_default`` Boolean
``overwrite`` Booleans ``overwrite`` Booleans
``argname`` String or table ``argname`` String or table
``choices`` Table
``action`` Function or string ``action`` Function or string
``init`` Any ``init`` Any
``hidden`` Boolean ``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. 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 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'") assert.has_error(function() parser:parse{} end, "missing argument 'foo1'")
end) 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)
end) end)

View File

@@ -290,6 +290,16 @@ describe("tests related to options", function()
end, "too many arguments") end, "too many arguments")
end) 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() it("doesn't accept GNU-like long options when it doesn't need arguments", function()
local parser = Parser() local parser = Parser()
parser:flag "-q" "--quiet" parser:flag "-q" "--quiet"

View File

@@ -341,6 +341,7 @@ local Argument = class({
typechecked("defmode", "string"), typechecked("defmode", "string"),
typechecked("show_default", "boolean"), typechecked("show_default", "boolean"),
typechecked("argname", "string", "table"), typechecked("argname", "string", "table"),
typechecked("choices", "table"),
typechecked("hidden", "boolean"), typechecked("hidden", "boolean"),
option_action, option_action,
option_init option_init
@@ -363,6 +364,7 @@ local Option = class({
typechecked("show_default", "boolean"), typechecked("show_default", "boolean"),
typechecked("overwrite", "boolean"), typechecked("overwrite", "boolean"),
typechecked("argname", "string", "table"), typechecked("argname", "string", "table"),
typechecked("choices", "table"),
typechecked("hidden", "boolean"), typechecked("hidden", "boolean"),
option_action, option_action,
option_init option_init
@@ -1204,7 +1206,21 @@ function ElementState:invoke()
return self.open return self.open
end 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) function ElementState:pass(argument)
self:check_choices(argument)
argument = self:convert(argument, #self.args + 1) argument = self:convert(argument, #self.args + 1)
table.insert(self.args, argument) table.insert(self.args, argument)