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

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)

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
local function complete_invocation(element)
while passed[element] < element._minargs do
pass(element, element._default)
end
end
function close(element) function close(element)
if passed[element] < element._minargs then if passed[element] < element._minargs then
if element._default then if element._default and element._defmode:find "a" then
while passed[element] < element._minargs do complete_invocation(element)
pass(element, element._default)
end
else else
error_("too few arguments") error_("too few arguments")
end end
@@ -760,7 +766,11 @@ function Parser:_parse(args, errhandler)
end end
while cur_arg do while cur_arg do
close(cur_arg) if passed[cur_arg] == 0 and cur_arg._default and cur_arg._defmode:find "c" then
complete_invocation(cur_arg)
else
close(cur_arg)
end
end end
if parser._require_command and #commands > 0 then if parser._require_command and #commands > 0 then
@@ -768,8 +778,16 @@ function Parser:_parse(args, errhandler)
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)