Reworked default values

* Better out-of-the-box behavior: commonly used feature should work without configuration. Only use default value if argument/option was not used at all.
* Add `defmode` field so that old behaviour can be used, too.
This commit is contained in:
mpeterv
2014-02-28 17:06:07 +04:00
parent a4b93b0833
commit 57f8d46ba3
2 changed files with 94 additions and 33 deletions
+66 -23
View File
@@ -4,21 +4,33 @@ describe("tests related to default values", function()
describe("default values for arguments", function() describe("default values for arguments", function()
it("handles default argument correctly", function() it("handles default argument correctly", function()
local parser = Parser() local parser = Parser()
parser:argument("foo", { parser:argument "foo"
default = "bar" :default "bar"
}) local args = parser:parse{}
local args = parser:parse({})
assert.same({foo = "bar"}, args) assert.same({foo = "bar"}, args)
local args = parser:parse{"baz"}
assert.same({foo = "baz"}, args)
end) end)
it("handles default multi-argument correctly", function() it("handles default argument for multi-argument correctly", function()
local parser = Parser()
parser:argument("foo", {
args = 3,
default = "bar",
defmode = "arg"
})
local args = parser:parse{"baz"}
assert.same({foo = {"baz", "bar", "bar"}}, args)
end)
it("handles default value for multi-argument correctly", function()
local parser = Parser() local parser = Parser()
parser:argument("foo", { parser:argument("foo", {
args = 3, args = 3,
default = "bar" default = "bar"
}) })
local args = parser:parse({"baz"}) local args = parser:parse{}
assert.same({foo = {"baz", "bar", "bar"}}, args) assert.same({foo = {"bar", "bar", "bar"}}, args)
end) end)
it("does not use default values if not needed", function() it("does not use default values if not needed", function()
@@ -34,29 +46,57 @@ describe("tests related to default values", function()
describe("default values for options", function() describe("default values for options", function()
it("handles option with default value correctly", function() it("handles option with default value correctly", function()
local parser = Parser()
parser:option("-f", "--foo", {
default = "bar"
})
local args = parser:parse({"-f"})
assert.same({foo = "bar"}, args)
end)
it("handles underused option with default value correctly", function()
local parser = Parser() local parser = Parser()
parser:option "-o" "--output" parser:option "-o" "--output"
:count(1)
:default "a.out" :default "a.out"
local args = parser:parse{} local args = parser:parse{}
assert.same({output = "a.out"}, args) assert.same({output = "a.out"}, args)
args = parser:parse{"--output", "foo.txt"}
assert.same({output = "foo.txt"}, args)
assert.has_error(function() parser:parse{"-o"} end, "too few arguments")
end) end)
it("doesn't use default if option is not invoked", function() it("handles option with default value for multi-argument option correctly", function()
local parser = Parser()
parser:option("-s", "--several", {
default = "foo",
args = "2-3"
})
local args = parser:parse{}
assert.same({several = {"foo", "foo"}}, args)
end)
it("handles option with default value and argument", function()
local parser = Parser()
parser:option("-o", "--output", {
default = "a.out",
defmode = "arg+count"
})
local args = parser:parse{}
assert.same({output = "a.out"}, args)
args = parser:parse{"-o"}
assert.same({output = "a.out"}, args)
args = parser:parse{"-o", "value"}
assert.same({output = "value"}, args)
end)
it("handles option with default argument correctly", function()
local parser = Parser()
parser:option "-p" "--protected"
:target "password"
:default "password"
:defmode "arg"
local args = parser:parse{"-p"}
assert.same({password = "password"}, args)
end)
it("doesn't use default argument if option is not invoked", function()
local parser = Parser() local parser = Parser()
parser:option("-f", "--foo", { parser:option("-f", "--foo", {
default = "bar" default = "bar",
defmode = "arg"
}) })
local args = parser:parse({}) local args = parser:parse{}
assert.same({}, args) assert.same({}, args)
end) end)
@@ -64,7 +104,8 @@ describe("tests related to default values", function()
local parser = Parser() local parser = Parser()
parser:option("-f", "--foo", { parser:option("-f", "--foo", {
args = 3, args = 3,
default = "bar" default = "bar",
defmode = "arg"
}) })
local args = parser:parse({"--foo=baz"}) local args = parser:parse({"--foo=baz"})
assert.same({foo = {"baz", "bar", "bar"}}, args) assert.same({foo = {"baz", "bar", "bar"}}, args)
@@ -74,7 +115,8 @@ describe("tests related to default values", function()
local parser = Parser() local parser = Parser()
parser:option("-f", "--foo", { parser:option("-f", "--foo", {
args = "1-2", args = "1-2",
default = "bar" default = "bar",
defmode = "arg"
}) })
local args = parser:parse({"-f", "baz"}) local args = parser:parse({"-f", "baz"})
assert.same({foo = {"baz"}}, args) assert.same({foo = {"baz"}}, args)
@@ -84,7 +126,8 @@ describe("tests related to default values", function()
local parser = Parser() local parser = Parser()
parser:option("-f", "--foo", { parser:option("-f", "--foo", {
count = "*", count = "*",
default = "bar" default = "bar",
defmode = "arg + count"
}) })
local args = parser:parse({"-f", "--foo=baz", "--foo"}) local args = parser:parse({"-f", "--foo=baz", "--foo"})
assert.same({foo = {"bar", "baz", "bar"}}, args) assert.same({foo = {"bar", "baz", "bar"}}, args)
+26 -8
View File
@@ -70,10 +70,11 @@ local Argument = class {
__name = "Argument", __name = "Argument",
_args = 1, _args = 1,
_count = 1, _count = 1,
_defmode = "count",
_fields = { _fields = {
"name", "description", "target", "args", "name", "description", "target", "args",
"minargs", "maxargs", "default", "convert", "minargs", "maxargs", "default", "defmode",
"usage", "argname" "convert", "usage", "argname"
} }
}:include(Declarative) }:include(Declarative)
@@ -85,8 +86,9 @@ local Option = Argument:extends {
_fields = { _fields = {
"name", "aliases", "description", "target", "name", "aliases", "description", "target",
"args", "minargs", "maxargs", "count", "args", "minargs", "maxargs", "count",
"mincount", "maxcount", "default", "convert", "mincount", "maxcount", "default", "defmode",
"overwrite", "action", "usage", "argname" "convert", "overwrite", "action", "usage",
"argname"
} }
} }
@@ -603,12 +605,16 @@ function Parser:_parse(args, errhandler)
end end
end end
function close(element) local function complete_invocation(element)
if passed[element] < element._minargs then
if element._default then
while passed[element] < element._minargs do while passed[element] < element._minargs do
pass(element, element._default) pass(element, element._default)
end end
end
function close(element)
if passed[element] < element._minargs then
if element._default and element._defmode:find "a" then
complete_invocation(element)
else else
error_("too few arguments") error_("too few arguments")
end end
@@ -760,16 +766,28 @@ function Parser:_parse(args, errhandler)
end end
while cur_arg do while cur_arg do
if passed[cur_arg] == 0 and cur_arg._default and cur_arg._defmode:find "c" then
complete_invocation(cur_arg)
else
close(cur_arg) close(cur_arg)
end end
end
if parser._require_command and #commands > 0 then if parser._require_command and #commands > 0 then
error_("a command is required") error_("a command is required")
end end
for _, option in ipairs(options) do for _, option in ipairs(options) do
if invocations[option] == 0 then
if option._default and option._defmode:find "c" then
invoke(option)
complete_invocation(option)
close(option)
end
end
if invocations[option] < option._mincount then if invocations[option] < option._mincount then
if option._default then if option._default and option._defmode:find "a" then
while invocations[option] < option._mincount do while invocations[option] < option._mincount do
invoke(option) invoke(option)
close(option) close(option)