Add Trace() function.
This commit is contained in:
@@ -33,7 +33,7 @@ func build(ast interface{}) (app *Application, err error) {
|
|||||||
Decoder: kindDecoders[reflect.Bool],
|
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 {
|
if len(node.Positional) > 0 && len(node.Children) > 0 {
|
||||||
return nil, fmt.Errorf("can't mix positional arguments and branching arguments on %T", ast)
|
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), "-")
|
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{}
|
node := &Node{}
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
ft := v.Type().Field(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"))
|
tag := parseTag(fv, ft.Tag.Get("kong"))
|
||||||
|
|
||||||
if !cmd {
|
|
||||||
cmd = tag.Cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nested structs are either commands or args.
|
// 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)
|
buildChild(node, v, ft, fv, tag, name, seenFlags)
|
||||||
} else {
|
} else {
|
||||||
buildField(node, v, ft, fv, tag, name, seenFlags)
|
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) {
|
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
|
child.Help = tag.Help
|
||||||
|
|
||||||
// A branching argument. This is a bit hairy, as we let buildNode() do the parsing, then check that
|
// 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 {
|
type ParseContext struct {
|
||||||
Trace []*ParseTrace // A trace through parsed nodes.
|
Trace []*ParseTrace // A trace through parsed nodes.
|
||||||
|
Error error // Error that occurred during trace, if any.
|
||||||
|
|
||||||
command []string // Full command path.
|
command []string // Full command path.
|
||||||
flags []*Flag // Accumulated available flags.
|
flags []*Flag // Accumulated available flags.
|
||||||
@@ -40,7 +41,8 @@ func Trace(args []string, app *Application) (*ParseContext, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// FlagValue returns the set value of a flag, if it was encountered and exists.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const defaultHelp = `{{- with .Application -}}
|
|||||||
usage: {{.Name}}
|
usage: {{.Name}}
|
||||||
|
|
||||||
{{.Help}}
|
{{.Help}}
|
||||||
{{range .Flags}}
|
{{range .Context.Flags}}
|
||||||
--{{.Name}}
|
--{{.Name}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,30 @@ func (k *Kong) Parse(args []string) (command string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
if ctx.Error != nil {
|
||||||
|
return "", ctx.Error
|
||||||
|
}
|
||||||
if value := ctx.FlagValue(k.Model.HelpFlag); value.IsValid() && value.Bool() {
|
if value := ctx.FlagValue(k.Model.HelpFlag); value.IsValid() && value.Bool() {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return ctx.Apply()
|
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{}) {
|
func (k *Kong) Errorf(format string, args ...interface{}) {
|
||||||
fmt.Fprintf(os.Stderr, k.Model.Name+": "+format, args...)
|
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)
|
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) {
|
func TestDuplicateFlag(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Flag bool
|
Flag bool
|
||||||
Cmd struct {
|
Cmd struct {
|
||||||
Flag bool
|
Flag bool
|
||||||
}
|
} `kong:"cmd"`
|
||||||
}
|
}
|
||||||
_, err := New(&cli)
|
_, err := New(&cli)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -336,11 +344,24 @@ func TestDuplicateFlagOnPeerCommandIsOkay(t *testing.T) {
|
|||||||
var cli struct {
|
var cli struct {
|
||||||
Cmd1 struct {
|
Cmd1 struct {
|
||||||
Flag bool
|
Flag bool
|
||||||
}
|
} `kong:"cmd"`
|
||||||
Cmd2 struct {
|
Cmd2 struct {
|
||||||
Flag bool
|
Flag bool
|
||||||
}
|
} `kong:"cmd"`
|
||||||
}
|
}
|
||||||
_, err := New(&cli)
|
_, err := New(&cli)
|
||||||
require.NoError(t, err)
|
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