command: root early handler func special case
This allows for early initialisation with access to flags on the root node. This can be useful for configuring global state used by subcommands. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
54308f79d2
commit
312753924b
@ -7,8 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// New initialises a root Node.
|
// New initialises a root Node.
|
||||||
func New(output io.Writer, logf LogFunc, name string) Command {
|
func New(output io.Writer, logf LogFunc, name string, early HandlerFunc) Command {
|
||||||
return rootNode{newNode(output, logf, name, "")}
|
c := rootNode{newNode(output, logf, name, "")}
|
||||||
|
c.f = early
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNode(output io.Writer, logf LogFunc, name, usage string) *node {
|
func newNode(output io.Writer, logf LogFunc, name, usage string) *node {
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestBuild(t *testing.T) {
|
func TestBuild(t *testing.T) {
|
||||||
c := command.New(nil, nil, "test")
|
c := command.New(nil, nil, "test", nil)
|
||||||
stubHandler := func([]string) error { panic("unreachable") }
|
stubHandler := func([]string) error { panic("unreachable") }
|
||||||
|
|
||||||
t.Run("nil direct handler", func(t *testing.T) {
|
t.Run("nil direct handler", func(t *testing.T) {
|
||||||
|
@ -34,9 +34,6 @@ func (n *node) Parse(arguments []string) error {
|
|||||||
|
|
||||||
match:
|
match:
|
||||||
if n.child != nil {
|
if n.child != nil {
|
||||||
if n.f != nil {
|
|
||||||
panic("invalid subcommand tree state")
|
|
||||||
}
|
|
||||||
// propagate help prefix early: flag set usage dereferences help
|
// propagate help prefix early: flag set usage dereferences help
|
||||||
n.child.prefix = append(n.prefix, n.name)
|
n.child.prefix = append(n.prefix, n.name)
|
||||||
}
|
}
|
||||||
@ -50,6 +47,17 @@ match:
|
|||||||
args := n.set.Args()
|
args := n.set.Args()
|
||||||
|
|
||||||
if n.child != nil {
|
if n.child != nil {
|
||||||
|
if n.f != nil {
|
||||||
|
if n.usage != "" { // root node early special case
|
||||||
|
panic("invalid subcommand tree state")
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case: root node calls HandlerFunc for initialisation
|
||||||
|
if err := n.f(nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return n.writeHelp()
|
return n.writeHelp()
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,13 @@ func TestParse(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"d=0 empty sub",
|
"d=0 empty sub",
|
||||||
func(wout, wlog io.Writer) command.Command { return command.New(wout, newLogFunc(wlog), "root") },
|
func(wout, wlog io.Writer) command.Command { return command.New(wout, newLogFunc(wlog), "root", nil) },
|
||||||
[]string{""},
|
[]string{""},
|
||||||
"", "test: \"root\" has no subcommands\n", command.ErrEmptyTree,
|
"", "test: \"root\" has no subcommands\n", command.ErrEmptyTree,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"d=0 empty sub garbage",
|
"d=0 empty sub garbage",
|
||||||
func(wout, wlog io.Writer) command.Command { return command.New(wout, newLogFunc(wlog), "root") },
|
func(wout, wlog io.Writer) command.Command { return command.New(wout, newLogFunc(wlog), "root", nil) },
|
||||||
[]string{"a", "b", "c", "d"},
|
[]string{"a", "b", "c", "d"},
|
||||||
"", "test: \"root\" has no subcommands\n", command.ErrEmptyTree,
|
"", "test: \"root\" has no subcommands\n", command.ErrEmptyTree,
|
||||||
},
|
},
|
||||||
@ -77,6 +77,18 @@ func TestParse(t *testing.T) {
|
|||||||
"flag provided but not defined: -val\n\nUsage:\ttest flag [-h | --help] COMMAND [OPTIONS]\n\n", "",
|
"flag provided but not defined: -val\n\nUsage:\ttest flag [-h | --help] COMMAND [OPTIONS]\n\n", "",
|
||||||
errors.New("flag provided but not defined: -val"),
|
errors.New("flag provided but not defined: -val"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"d=0 bool flag",
|
||||||
|
buildTestCommand,
|
||||||
|
[]string{"-v", "succeed"},
|
||||||
|
"", "test: verbose\n", nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"d=0 bool flag early error",
|
||||||
|
buildTestCommand,
|
||||||
|
[]string{"--fail", "succeed"},
|
||||||
|
"", "", errSuccess,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"d=1 empty sub",
|
"d=1 empty sub",
|
||||||
@ -126,7 +138,7 @@ func TestParse(t *testing.T) {
|
|||||||
buildTestCommand,
|
buildTestCommand,
|
||||||
[]string{},
|
[]string{},
|
||||||
`
|
`
|
||||||
Usage: test [-h | --help] [-v] [--val <value>] COMMAND [OPTIONS]
|
Usage: test [-h | --help] [-v] [--fail] [--val <value>] COMMAND [OPTIONS]
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
error return an error
|
error return an error
|
||||||
@ -144,7 +156,7 @@ Commands:
|
|||||||
buildTestCommand,
|
buildTestCommand,
|
||||||
[]string{"-h"},
|
[]string{"-h"},
|
||||||
`
|
`
|
||||||
Usage: test [-h | --help] [-v] [--val <value>] COMMAND [OPTIONS]
|
Usage: test [-h | --help] [-v] [--fail] [--val <value>] COMMAND [OPTIONS]
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
error return an error
|
error return an error
|
||||||
@ -156,7 +168,9 @@ Commands:
|
|||||||
deep top level of command tree with various levels
|
deep top level of command tree with various levels
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-v verbosity
|
-fail
|
||||||
|
fail early
|
||||||
|
-v verbose output
|
||||||
-val string
|
-val string
|
||||||
store val for the "flag" command (default "default")
|
store val for the "flag" command (default "default")
|
||||||
|
|
||||||
@ -239,11 +253,24 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func buildTestCommand(wout, wlog io.Writer) (c command.Command) {
|
func buildTestCommand(wout, wlog io.Writer) (c command.Command) {
|
||||||
var val string
|
var (
|
||||||
|
flagVerbose bool
|
||||||
|
flagFail bool
|
||||||
|
flagVal string
|
||||||
|
)
|
||||||
|
|
||||||
logf := newLogFunc(wlog)
|
logf := newLogFunc(wlog)
|
||||||
c = command.New(wout, logf, "test").
|
c = command.New(wout, logf, "test", func([]string) error {
|
||||||
Flag(new(bool), "v", command.BoolFlag(false), "verbosity").
|
if flagVerbose {
|
||||||
|
logf("verbose")
|
||||||
|
}
|
||||||
|
if flagFail {
|
||||||
|
return errSuccess
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}).
|
||||||
|
Flag(&flagVerbose, "v", command.BoolFlag(false), "verbose output").
|
||||||
|
Flag(&flagFail, "fail", command.BoolFlag(false), "fail early").
|
||||||
Command("error", "return an error", func([]string) error {
|
Command("error", "return an error", func([]string) error {
|
||||||
return errSuccess
|
return errSuccess
|
||||||
}).
|
}).
|
||||||
@ -255,9 +282,9 @@ func buildTestCommand(wout, wlog io.Writer) (c command.Command) {
|
|||||||
_, err := fmt.Fprint(wout, a...)
|
_, err := fmt.Fprint(wout, a...)
|
||||||
return err
|
return err
|
||||||
}).
|
}).
|
||||||
Flag(&val, "val", command.StringFlag("default"), "store val for the \"flag\" command").
|
Flag(&flagVal, "val", command.StringFlag("default"), "store val for the \"flag\" command").
|
||||||
Command("flag", "print value passed by flag", func(args []string) error {
|
Command("flag", "print value passed by flag", func(args []string) error {
|
||||||
_, err := fmt.Fprint(wout, val)
|
_, err := fmt.Fprint(wout, flagVal)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ func TestParseUnreachable(t *testing.T) {
|
|||||||
n := newNode(panicWriter{}, nil, " ", " ")
|
n := newNode(panicWriter{}, nil, " ", " ")
|
||||||
n.adopt(newNode(panicWriter{}, nil, " ", " "))
|
n.adopt(newNode(panicWriter{}, nil, " ", " "))
|
||||||
n.f = func([]string) error { panic("unreachable") }
|
n.f = func([]string) error { panic("unreachable") }
|
||||||
_ = n.Parse(nil)
|
_ = n.Parse([]string{" "})
|
||||||
})
|
})
|
||||||
|
|
||||||
// this would only happen if a node was matched twice
|
// this would only happen if a node was matched twice
|
||||||
|
Loading…
Reference in New Issue
Block a user