ShareDAV/vendor/github.com/alecthomas/kong/model.go

434 lines
10 KiB
Go

package kong
import (
"fmt"
"math"
"os"
"reflect"
"strconv"
"strings"
"github.com/pkg/errors"
)
// A Visitable component in the model.
type Visitable interface {
node()
}
// Application is the root of the Kong model.
type Application struct {
*Node
// Help flag, if the NoDefaultHelp() option is not specified.
HelpFlag *Flag
}
// Argument represents a branching positional argument.
type Argument = Node
// Command represents a command in the CLI.
type Command = Node
// NodeType is an enum representing the type of a Node.
type NodeType int
// Node type enumerations.
const (
ApplicationNode NodeType = iota
CommandNode
ArgumentNode
)
// Node is a branch in the CLI. ie. a command or positional argument.
type Node struct {
Type NodeType
Parent *Node
Name string
Help string // Short help displayed in summaries.
Detail string // Detailed help displayed when describing command/arg alone.
Group string
Hidden bool
Flags []*Flag
Positional []*Positional
Children []*Node
Target reflect.Value // Pointer to the value in the grammar that this Node is associated with.
Tag *Tag
Argument *Value // Populated when Type is ArgumentNode.
}
func (*Node) node() {}
// Leaf returns true if this Node is a leaf node.
func (n *Node) Leaf() bool {
return len(n.Children) == 0
}
// Find a command/argument/flag by pointer to its field.
//
// Returns nil if not found. Panics if ptr is not a pointer.
func (n *Node) Find(ptr interface{}) *Node {
key := reflect.ValueOf(ptr)
if key.Kind() != reflect.Ptr {
panic("expected a pointer")
}
return n.findNode(key)
}
func (n *Node) findNode(key reflect.Value) *Node {
if n.Target == key {
return n
}
for _, child := range n.Children {
if found := child.findNode(key); found != nil {
return found
}
}
return nil
}
// AllFlags returns flags from all ancestor branches encountered.
//
// If "hide" is true hidden flags will be omitted.
func (n *Node) AllFlags(hide bool) (out [][]*Flag) {
if n.Parent != nil {
out = append(out, n.Parent.AllFlags(hide)...)
}
group := []*Flag{}
for _, flag := range n.Flags {
if !hide || !flag.Hidden {
group = append(group, flag)
}
}
if len(group) > 0 {
out = append(out, group)
}
return
}
// Leaves returns the leaf commands/arguments under Node.
//
// If "hidden" is true hidden leaves will be omitted.
func (n *Node) Leaves(hide bool) (out []*Node) {
_ = Visit(n, func(nd Visitable, next Next) error {
if nd == n {
return next(nil)
}
if node, ok := nd.(*Node); ok {
if hide && node.Hidden {
return nil
}
if len(node.Children) == 0 && node.Type != ApplicationNode {
out = append(out, node)
}
}
return next(nil)
})
return
}
// Depth of the command from the application root.
func (n *Node) Depth() int {
depth := 0
p := n.Parent
for p != nil && p.Type != ApplicationNode {
depth++
p = p.Parent
}
return depth
}
// Summary help string for the node (not including application name).
func (n *Node) Summary() string {
summary := n.Path()
if flags := n.FlagSummary(true); flags != "" {
summary += " " + flags
}
args := []string{}
for _, arg := range n.Positional {
args = append(args, arg.Summary())
}
if len(args) != 0 {
summary += " " + strings.Join(args, " ")
} else if len(n.Children) > 0 {
summary += " <command>"
}
return summary
}
// FlagSummary for the node.
func (n *Node) FlagSummary(hide bool) string {
required := []string{}
count := 0
for _, group := range n.AllFlags(hide) {
for _, flag := range group {
count++
if flag.Required {
required = append(required, flag.Summary())
}
}
}
return strings.Join(required, " ")
}
// FullPath is like Path() but includes the Application root node.
func (n *Node) FullPath() string {
root := n
for root.Parent != nil {
root = root.Parent
}
return strings.TrimSpace(root.Name + " " + n.Path())
}
// Vars returns the combined Vars defined by all ancestors of this Node.
func (n *Node) Vars() Vars {
if n == nil {
return Vars{}
}
return n.Parent.Vars().CloneWith(n.Tag.Vars)
}
// Path through ancestors to this Node.
func (n *Node) Path() (out string) {
if n.Parent != nil {
out += " " + n.Parent.Path()
}
switch n.Type {
case CommandNode:
out += " " + n.Name
case ArgumentNode:
out += " " + "<" + n.Name + ">"
}
return strings.TrimSpace(out)
}
// A Value is either a flag or a variable positional argument.
type Value struct {
Flag *Flag // Nil if positional argument.
Name string
Help string
Default string
DefaultValue reflect.Value
Enum string
Mapper Mapper
Tag *Tag
Target reflect.Value
Required bool
Set bool // Set to true when this value is set through some mechanism.
Format string // Formatting directive, if applicable.
Position int // Position (for positional arguments).
}
// EnumMap returns a map of the enums in this value.
func (v *Value) EnumMap() map[string]bool {
parts := strings.Split(v.Enum, ",")
out := make(map[string]bool, len(parts))
for _, part := range parts {
out[strings.TrimSpace(part)] = true
}
return out
}
// ShortSummary returns a human-readable summary of the value, not including any placeholders/defaults.
func (v *Value) ShortSummary() string {
if v.Flag != nil {
return fmt.Sprintf("--%s", v.Name)
}
argText := "<" + v.Name + ">"
if v.IsCumulative() {
argText += " ..."
}
if !v.Required {
argText = "[" + argText + "]"
}
return argText
}
// Summary returns a human-readable summary of the value.
func (v *Value) Summary() string {
if v.Flag != nil {
if v.IsBool() {
return fmt.Sprintf("--%s", v.Name)
}
return fmt.Sprintf("--%s=%s", v.Name, v.Flag.FormatPlaceHolder())
}
argText := "<" + v.Name + ">"
if v.IsCumulative() {
argText += " ..."
}
if !v.Required {
argText = "[" + argText + "]"
}
return argText
}
// IsCumulative returns true if the type can be accumulated into.
func (v *Value) IsCumulative() bool {
return v.IsSlice() || v.IsMap()
}
// IsSlice returns true if the value is a slice.
func (v *Value) IsSlice() bool {
return v.Target.Type().Name() == "" && v.Target.Kind() == reflect.Slice
}
// IsMap returns true if the value is a map.
func (v *Value) IsMap() bool {
return v.Target.Kind() == reflect.Map
}
// IsBool returns true if the underlying value is a boolean.
func (v *Value) IsBool() bool {
if m, ok := v.Mapper.(BoolMapper); ok && m.IsBool() {
return true
}
return v.Target.Kind() == reflect.Bool
}
// Parse tokens into value, parse, and validate, but do not write to the field.
func (v *Value) Parse(scan *Scanner, target reflect.Value) (err error) {
defer func() {
if rerr := recover(); rerr != nil {
switch rerr := rerr.(type) {
case Error:
err = errors.Wrap(rerr, v.ShortSummary())
default:
panic(fmt.Sprintf("mapper %T failed to apply to %s: %s", v.Mapper, v.Summary(), rerr))
}
}
}()
err = v.Mapper.Decode(&DecodeContext{Value: v, Scan: scan}, target)
if err != nil {
return errors.Wrap(err, v.ShortSummary())
}
v.Set = true
return nil
}
// Apply value to field.
func (v *Value) Apply(value reflect.Value) {
v.Target.Set(value)
v.Set = true
}
// ApplyDefault value to field if it is not already set.
func (v *Value) ApplyDefault() error {
if reflectValueIsZero(v.Target) {
return v.Reset()
}
v.Set = true
return nil
}
// Reset this value to its default, either the zero value or the parsed result of its envar,
// or its "default" tag.
//
// Does not include resolvers.
func (v *Value) Reset() error {
v.Target.Set(reflect.Zero(v.Target.Type()))
if v.Tag.Env != "" {
envar := os.Getenv(v.Tag.Env)
if envar != "" {
err := v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: envar}), v.Target)
if err != nil {
return fmt.Errorf("%s (from envar %s=%q)", err, v.Tag.Env, envar)
}
return nil
}
}
if v.Default != "" {
return v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: v.Default}), v.Target)
}
return nil
}
func (*Value) node() {}
// A Positional represents a non-branching command-line positional argument.
type Positional = Value
// A Flag represents a command-line flag.
type Flag struct {
*Value
Group string // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
Xor string
PlaceHolder string
Env string
Short rune
Hidden bool
}
func (f *Flag) String() string {
out := "--" + f.Name
if f.Short != 0 {
out = fmt.Sprintf("-%c, %s", f.Short, out)
}
if !f.IsBool() {
out += "=" + f.FormatPlaceHolder()
}
return out
}
// FormatPlaceHolder formats the placeholder string for a Flag.
func (f *Flag) FormatPlaceHolder() string {
tail := ""
if f.Value.IsSlice() && f.Value.Tag.Sep != -1 {
tail += string(f.Value.Tag.Sep) + "..."
}
if f.Default != "" {
if f.Value.Target.Kind() == reflect.String {
return strconv.Quote(f.Default) + tail
}
return f.Default + tail
}
if f.PlaceHolder != "" {
return f.PlaceHolder + tail
}
if f.Value.IsMap() {
if f.Value.Tag.MapSep != -1 {
tail = string(f.Value.Tag.MapSep) + "..."
}
return "KEY=VALUE" + tail
}
return strings.ToUpper(f.Name) + tail
}
// This is directly from the Go 1.13 source code.
func reflectValueIsZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return math.Float64bits(v.Float()) == 0
case reflect.Complex64, reflect.Complex128:
c := v.Complex()
return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
case reflect.Array:
for i := 0; i < v.Len(); i++ {
if !reflectValueIsZero(v.Index(i)) {
return false
}
}
return true
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
return v.IsNil()
case reflect.String:
return v.Len() == 0
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
if !reflectValueIsZero(v.Field(i)) {
return false
}
}
return true
default:
// This should never happens, but will act as a safeguard for
// later, as a default value doesn't makes sense here.
panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()})
}
}