Convert Hook to an Option.

This commit is contained in:
Alec Thomas
2018-05-27 13:37:17 -04:00
committed by Gerald Kaszuba
parent cf89213e1e
commit afbb431641
4 changed files with 56 additions and 28 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ usage: {{.Name}}
var defaultHelpTemplate = template.Must(template.New("help").Parse(defaultHelp)) var defaultHelpTemplate = template.Must(template.New("help").Parse(defaultHelp))
// Help returns a Hook that will display help and exit. // Help returns a Hook that will display help and exit.
func Help(tmpl *template.Template, tmplctx map[string]interface{}) Hook { func Help(tmpl *template.Template, tmplctx map[string]interface{}) HookFunction {
return func(app *Kong, ctx *Context, trace *Trace) error { return func(app *Kong, ctx *Context, trace *Trace) error {
merged := map[string]interface{}{ merged := map[string]interface{}{
"Application": app.Model, "Application": app.Model,
+26 -12
View File
@@ -9,7 +9,7 @@ import (
"text/template" "text/template"
) )
type Hook func(app *Kong, ctx *Context, trace *Trace) error type HookFunction func(app *Kong, ctx *Context, trace *Trace) error
// Error reported by Kong. // Error reported by Kong.
type Error struct{ msg string } type Error struct{ msg string }
@@ -37,10 +37,11 @@ type Kong struct {
Stdout io.Writer Stdout io.Writer
Stderr io.Writer Stderr io.Writer
help *template.Template help *template.Template
helpContext map[string]interface{} helpContext map[string]interface{}
helpFuncs template.FuncMap helpFuncs template.FuncMap
hooks map[reflect.Value]Hook hooks map[reflect.Value]HookFunction
noDefaultHelp bool
} }
// New creates a new Kong parser into ast. // New creates a new Kong parser into ast.
@@ -52,7 +53,7 @@ func New(ast interface{}, options ...Option) (*Kong, error) {
help: defaultHelpTemplate, help: defaultHelpTemplate,
helpContext: map[string]interface{}{}, helpContext: map[string]interface{}{},
helpFuncs: template.FuncMap{}, helpFuncs: template.FuncMap{},
hooks: map[reflect.Value]Hook{}, hooks: map[reflect.Value]HookFunction{},
} }
model, err := build(ast) model, err := build(ast)
@@ -66,9 +67,28 @@ func New(ast interface{}, options ...Option) (*Kong, error) {
option(k) option(k)
} }
if !k.noDefaultHelp {
k.integrateHelp()
}
return k, nil return k, nil
} }
func (k *Kong) integrateHelp() {
helpValue := false
help := &Flag{
Value: Value{
Name: "help",
Help: "Show context-sensitive help.",
Flag: true,
Value: reflect.ValueOf(&helpValue).Elem(),
Decoder: kindDecoders[reflect.Bool],
},
}
k.Model.Flags = append([]*Flag{help}, k.Model.Flags...)
Hook(&helpValue, Help(defaultHelpTemplate, nil))(k)
}
// Trace parses the command-line, validating and collecting matching grammar nodes. // Trace parses the command-line, validating and collecting matching grammar nodes.
func (k *Kong) Trace(args []string) (*Context, error) { func (k *Kong) Trace(args []string) (*Context, error) {
p := &Context{ p := &Context{
@@ -89,12 +109,6 @@ func (k *Kong) Trace(args []string) (*Context, error) {
return p, nil return p, nil
} }
// Hook to execute when a command is encountered.
func (k *Kong) Hook(ptr interface{}, hook Hook) *Kong {
k.hooks[reflect.ValueOf(ptr)] = hook
return k
}
// Parse arguments into target. // Parse arguments into target.
// //
// The returned "command" is a space separated path to the final selected command, if any. Commands appear as // The returned "command" is a space separated path to the final selected command, if any. Commands appear as
+10 -15
View File
@@ -7,11 +7,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func mustNew(t *testing.T, cli interface{}) *Kong { func mustNew(t *testing.T, cli interface{}, options ...Option) *Kong {
t.Helper() t.Helper()
parser, err := New(cli, ExitFunction(func(int) { options = append(options, ExitFunction(func(int) {
t.Fatalf("unexpected exit()") t.Fatalf("unexpected exit()")
})) }))
parser, err := New(cli, options...)
require.NoError(t, err) require.NoError(t, err)
return parser return parser
} }
@@ -381,19 +382,13 @@ func TestHooks(t *testing.T) {
{"Flag", "one --three=three", values{true, "", "three"}}, {"Flag", "one --three=three", values{true, "", "three"}},
{"ArgAndFlag", "one two --three=three", values{true, "two", "three"}}, {"ArgAndFlag", "one two --three=three", values{true, "two", "three"}},
} }
p := mustNew(t, &cli). setOne := func(app *Kong, ctx *Context, trace *Trace) error { hooked.one = true; return nil }
Hook(&cli.One, func(app *Kong, ctx *Context, trace *Trace) error { setTwo := func(app *Kong, ctx *Context, trace *Trace) error { hooked.two = trace.Value.String(); return nil }
hooked.one = true setThree := func(app *Kong, ctx *Context, trace *Trace) error { hooked.three = trace.Value.String(); return nil }
return nil p := mustNew(t, &cli,
}). Hook(&cli.One, setOne),
Hook(&cli.One.Two, func(app *Kong, ctx *Context, trace *Trace) error { Hook(&cli.One.Two, setTwo),
hooked.two = trace.Value.String() Hook(&cli.One.Three, setThree))
return nil
}).
Hook(&cli.One.Three, func(app *Kong, ctx *Context, trace *Trace) error {
hooked.three = trace.Value.String()
return nil
})
for _, test := range tests { for _, test := range tests {
hooked = values{} hooked = values{}
+19
View File
@@ -2,6 +2,7 @@ package kong
import ( import (
"io" "io"
"reflect"
"text/template" "text/template"
) )
@@ -12,6 +13,13 @@ func ExitFunction(exit func(int)) Option {
return func(k *Kong) { k.Exit = exit } return func(k *Kong) { k.Exit = exit }
} }
// NoDefaultHelp disables the default help flags.
func NoDefaultHelp() Option {
return func(k *Kong) {
k.noDefaultHelp = true
}
}
// Name overrides the application name. // Name overrides the application name.
func Name(name string) Option { func Name(name string) Option {
return func(k *Kong) { k.Model.Name = name } return func(k *Kong) { k.Model.Name = name }
@@ -41,3 +49,14 @@ func Writers(stdout, stderr io.Writer) Option {
k.Stderr = stderr k.Stderr = stderr
} }
} }
// Hook to execute when a command, flag or positional argument is encountered.
func Hook(ptr interface{}, hook HookFunction) Option {
key := reflect.ValueOf(ptr)
if key.Kind() != reflect.Ptr {
panic("expected a pointer")
}
return func(k *Kong) {
k.hooks[key] = hook
}
}