Merge remote-tracking branch 'luarocks/refs/pull/3/head'

This commit is contained in:
daurnimator
2019-05-31 22:40:18 +10:00
8 changed files with 151 additions and 3 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] {north,south,east,west}
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,27 @@ 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.
.. code-block:: lua
:linenos:
parser:option "--format"
:choices {"short", "medium", "full"}
.. code-block:: none
$ lua script.lua --format foo
.. code-block:: none
Usage: script.lua [-h] [--format {short,medium,full}]
Error: argument for option '--format' must be one of 'short', 'medium', 'full'
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

@@ -77,6 +77,34 @@ Options:
--config <config>]], parser:get_help()) --config <config>]], parser:get_help())
end) end)
it("creates correct help message for arguments with choices", function()
local parser = Parser "foo"
parser:argument "move"
:choices {"rock", "paper", "scissors"}
assert.equal([[
Usage: foo [-h] {rock,paper,scissors}
Arguments:
{rock,paper,scissors}
Options:
-h, --help Show this help message and exit.]], parser:get_help())
end)
it("creates correct help message for options with argument choices", function()
local parser = Parser "foo"
parser:option "--format"
:choices {"short", "medium", "full"}
assert.equal([[
Usage: foo [-h] [--format {short,medium,full}]
Options:
-h, --help Show this help message and exit.
--format {short,medium,full}]], parser:get_help())
end)
it("adds margin for multiline descriptions", function() it("adds margin for multiline descriptions", function()
local parser = Parser "foo" local parser = Parser "foo"
parser:flag "-v" parser:flag "-v"

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

@@ -89,6 +89,30 @@ Usage: foo <first> <second-and-third> <second-and-third>
) )
end) end)
it("creates correct usage message for arguments with choices", function()
local parser = Parser "foo"
:add_help(false)
parser:argument "move"
:choices {"rock", "paper", "scissors"}
assert.equal(
[=[Usage: foo {rock,paper,scissors}]=],
parser:get_usage()
)
end)
it("creates correct usage message for options with argument choices", function()
local parser = Parser "foo"
:add_help(false)
parser:option "--format"
:choices {"short", "medium", "full"}
assert.equal(
[=[Usage: foo [--format {short,medium,full}]]=],
parser:get_usage()
)
end)
it("creates correct usage message for commands", function() it("creates correct usage message for commands", function()
local parser = Parser "foo" local parser = Parser "foo"
:add_help(false) :add_help(false)

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
@@ -511,17 +513,33 @@ function Argument:_get_argname(narg)
end end
end end
function Argument:_get_choices_list()
return "{" .. table.concat(self._choices, ",") .. "}"
end
function Argument:_get_default_argname() function Argument:_get_default_argname()
return "<" .. self._name .. ">" if self._choices then
return self:_get_choices_list()
else
return "<" .. self._name .. ">"
end
end end
function Option:_get_default_argname() function Option:_get_default_argname()
return "<" .. self:_get_default_target() .. ">" if self._choices then
return self:_get_choices_list()
else
return "<" .. self:_get_default_target() .. ">"
end
end end
-- Returns labels to be shown in the help message. -- Returns labels to be shown in the help message.
function Argument:_get_label_lines() function Argument:_get_label_lines()
return {self._name} if self._choices then
return {self:_get_choices_list()}
else
return {self._name}
end
end end
function Option:_get_label_lines() function Option:_get_label_lines()
@@ -1204,7 +1222,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)