Add Trace() function.

This commit is contained in:
Alec Thomas
2018-05-25 00:05:04 -04:00
parent 93e21885b1
commit 0bb304449c
5 changed files with 50 additions and 14 deletions
+4 -9
View File
@@ -33,7 +33,7 @@ func build(ast interface{}) (app *Application, err error) {
Decoder: kindDecoders[reflect.Bool],
}},
}
node := buildNode(iv, map[string]bool{"help": true}, true)
node := buildNode(iv, map[string]bool{"help": true})
if len(node.Positional) > 0 && len(node.Children) > 0 {
return nil, fmt.Errorf("can't mix positional arguments and branching arguments on %T", ast)
}
@@ -47,7 +47,7 @@ func dashedString(s string) string {
return strings.Join(camelCase(s), "-")
}
func buildNode(v reflect.Value, seenFlags map[string]bool, cmd bool) *Node {
func buildNode(v reflect.Value, seenFlags map[string]bool) *Node {
node := &Node{}
for i := 0; i < v.NumField(); i++ {
ft := v.Type().Field(i)
@@ -63,12 +63,8 @@ func buildNode(v reflect.Value, seenFlags map[string]bool, cmd bool) *Node {
tag := parseTag(fv, ft.Tag.Get("kong"))
if !cmd {
cmd = tag.Cmd
}
// Nested structs are either commands or args.
if ft.Type.Kind() == reflect.Struct && (cmd || tag.Arg) {
if ft.Type.Kind() == reflect.Struct && (tag.Cmd || tag.Arg) {
buildChild(node, v, ft, fv, tag, name, seenFlags)
} else {
buildField(node, v, ft, fv, tag, name, seenFlags)
@@ -94,7 +90,7 @@ func buildNode(v reflect.Value, seenFlags map[string]bool, cmd bool) *Node {
}
func buildChild(node *Node, v reflect.Value, ft reflect.StructField, fv reflect.Value, tag *Tag, name string, seenFlags map[string]bool) {
child := buildNode(fv, seenFlags, false)
child := buildNode(fv, seenFlags)
child.Help = tag.Help
// A branching argument. This is a bit hairy, as we let buildNode() do the parsing, then check that
@@ -168,4 +164,3 @@ func buildField(node *Node, v reflect.Value, ft reflect.StructField, fv reflect.
})
}
}
+3 -1
View File
@@ -20,6 +20,7 @@ type ParseTrace struct {
type ParseContext struct {
Trace []*ParseTrace // A trace through parsed nodes.
Error error // Error that occurred during trace, if any.
command []string // Full command path.
flags []*Flag // Accumulated available flags.
@@ -40,7 +41,8 @@ func Trace(args []string, app *Application) (*ParseContext, error) {
if err != nil {
return nil, err
}
return p, p.trace(&p.app.Node)
p.Error = p.trace(&p.app.Node)
return p, nil
}
// FlagValue returns the set value of a flag, if it was encountered and exists.
+1 -1
View File
@@ -9,7 +9,7 @@ const defaultHelp = `{{- with .Application -}}
usage: {{.Name}}
{{.Help}}
{{range .Flags}}
{{range .Context.Flags}}
--{{.Name}}
{{end}}
+18
View File
@@ -69,12 +69,30 @@ func (k *Kong) Parse(args []string) (command string, err error) {
if err != nil {
return "", err
}
if ctx.Error != nil {
return "", ctx.Error
}
if value := ctx.FlagValue(k.Model.HelpFlag); value.IsValid() && value.Bool() {
return "", nil
}
return ctx.Apply()
}
// Trace through the command tree.
//
// The returned context will include a trace of all parsed objects encountered; flags, arguments, commands.
func (k *Kong) Trace(args []string) (ctx *ParseContext, err error) {
defer func() {
msg := recover()
if test, ok := msg.(Error); ok {
err = test
} else if msg != nil {
panic(msg)
}
}()
return Trace(args, k.Model)
}
func (k *Kong) Errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, k.Model.Name+": "+format, args...)
}
+24 -3
View File
@@ -321,12 +321,20 @@ func TestHelp(t *testing.T) {
require.NotEqual(t, "hello", cli.Flag)
}
func TestCommandMissingTagIsInvalid(t *testing.T) {
var cli struct {
One struct{}
}
_, err := New(&cli)
require.Error(t, err)
}
func TestDuplicateFlag(t *testing.T) {
var cli struct {
Flag bool
Cmd struct {
Flag bool
}
} `kong:"cmd"`
}
_, err := New(&cli)
require.Error(t, err)
@@ -336,11 +344,24 @@ func TestDuplicateFlagOnPeerCommandIsOkay(t *testing.T) {
var cli struct {
Cmd1 struct {
Flag bool
}
} `kong:"cmd"`
Cmd2 struct {
Flag bool
}
} `kong:"cmd"`
}
_, err := New(&cli)
require.NoError(t, err)
}
func TestTraceErrorPartiallySucceeds(t *testing.T) {
var cli struct {
One struct {
Two struct {
} `kong:"cmd"`
} `kong:"cmd"`
}
p := mustNew(t, &cli)
trace, err := p.Trace([]string{"one", "bad"})
require.NoError(t, err)
require.Error(t, trace.Error)
}