Add Trace() function.
This commit is contained in:
@@ -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
@@ -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.
|
||||
|
||||
@@ -9,7 +9,7 @@ const defaultHelp = `{{- with .Application -}}
|
||||
usage: {{.Name}}
|
||||
|
||||
{{.Help}}
|
||||
{{range .Flags}}
|
||||
{{range .Context.Flags}}
|
||||
--{{.Name}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user