Separate validation into a distinct step.

This allows help to be called even when the parse trace is invalid.
Without this, the command-line would have to be valid in order to use
help at all, which defeats the purpose.
This commit is contained in:
Alec Thomas
2018-05-29 16:42:53 +10:00
committed by Gerald Kaszuba
parent afbb431641
commit fdc7230e22
14 changed files with 569 additions and 225 deletions
+76 -14
View File
@@ -1,27 +1,70 @@
package kong
import "reflect"
import (
"reflect"
"strconv"
"strings"
)
type Application struct {
Node
HelpFlag *Flag
}
// A Branch is a command or positional argument that results in a branch in the command tree.
type Branch struct {
Command *Command
Argument *Argument
// Leaves returns the leaf commands/arguments in the command-line grammar.
func (a *Application) Leaves() (out []*Node) {
var walk func(n *Node)
walk = func(n *Node) {
if len(n.Children) == 0 {
out = append(out, n)
}
for _, child := range n.Children {
if child.Type == CommandNode || child.Type == ArgumentNode {
walk(child)
}
}
}
walk(&a.Node)
return
}
type Argument = Node
type Command = Node
type NodeType int
const (
ApplicationNode NodeType = iota
CommandNode
ArgumentNode
)
type Node struct {
Type NodeType
Parent *Node
Name string
Help string
Flags []*Flag
Positional []*Value
Children []*Branch
Target reflect.Value
Positional []*Positional
Children []*Node
Target reflect.Value // Pointer to the value in the grammar that this Node is associated with.
Argument *Value // Populated when Type is ArgumentNode.
}
// Path through ancestors to this Node.
func (n *Node) Path() (out string) {
if n.Parent != nil {
out += " " + n.Parent.Path()
}
switch n.Type {
case ApplicationNode, CommandNode:
out += " " + n.Name
case ArgumentNode:
out += " " + "<" + n.Name + ">"
}
return strings.TrimSpace(out)
}
// A Value is either a flag or a variable positional argument.
@@ -36,6 +79,11 @@ type Value struct {
Required bool
Set bool // Used with Required to test if a value has been given.
Format string // Formatting directive, if applicable.
Position int // Position (for positional arguments).
}
func (v *Value) IsBool() bool {
return v.Value.Kind() == reflect.Bool
}
// Parse tokens into value, parse, and validate, but do not write to the field.
@@ -69,14 +117,28 @@ func (v *Value) Reset() error {
type Positional = Value
type Argument struct {
Node
Argument *Value
}
type Flag struct {
Value
Placeholder string
PlaceHolder string
Env string
Short rune
Hidden bool
}
func (f *Flag) FormatPlaceHolder() string {
if f.PlaceHolder != "" {
return f.PlaceHolder
}
if f.Default != "" {
ellipsis := ""
if len(f.Default) > 1 {
ellipsis = "..."
}
if f.Value.Value.Kind() == reflect.String {
return strconv.Quote(f.Default) + ellipsis
}
return f.Default + ellipsis
}
return strings.ToUpper(f.Name)
}