github.com/maresnic/mr-kong@v1.0.0/model.go (about)

     1  package kong
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"os"
     7  	"reflect"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  // A Visitable component in the model.
    13  type Visitable interface {
    14  	node()
    15  }
    16  
    17  // Application is the root of the Kong model.
    18  type Application struct {
    19  	*Node
    20  	// Help flag, if the NoDefaultHelp() option is not specified.
    21  	HelpFlag *Flag
    22  }
    23  
    24  // Argument represents a branching positional argument.
    25  type Argument = Node
    26  
    27  // Command represents a command in the CLI.
    28  type Command = Node
    29  
    30  // NodeType is an enum representing the type of a Node.
    31  type NodeType int
    32  
    33  // Node type enumerations.
    34  const (
    35  	ApplicationNode NodeType = iota
    36  	CommandNode
    37  	ArgumentNode
    38  )
    39  
    40  // Node is a branch in the CLI. ie. a command or positional argument.
    41  type Node struct {
    42  	Type        NodeType
    43  	Parent      *Node
    44  	Name        string
    45  	Help        string // Short help displayed in summaries.
    46  	Detail      string // Detailed help displayed when describing command/arg alone.
    47  	Group       *Group
    48  	Hidden      bool
    49  	Flags       []*Flag
    50  	Positional  []*Positional
    51  	Children    []*Node
    52  	DefaultCmd  *Node
    53  	Target      reflect.Value // Pointer to the value in the grammar that this Node is associated with.
    54  	Tag         *Tag
    55  	Aliases     []string
    56  	Passthrough bool // Set to true to stop flag parsing when encountered.
    57  	Active      bool // Denotes the node is part of an active branch in the CLI.
    58  
    59  	Argument *Value // Populated when Type is ArgumentNode.
    60  }
    61  
    62  func (*Node) node() {}
    63  
    64  // Leaf returns true if this Node is a leaf node.
    65  func (n *Node) Leaf() bool {
    66  	return len(n.Children) == 0
    67  }
    68  
    69  // Find a command/argument/flag by pointer to its field.
    70  //
    71  // Returns nil if not found. Panics if ptr is not a pointer.
    72  func (n *Node) Find(ptr interface{}) *Node {
    73  	key := reflect.ValueOf(ptr)
    74  	if key.Kind() != reflect.Ptr {
    75  		panic("expected a pointer")
    76  	}
    77  	return n.findNode(key)
    78  }
    79  
    80  func (n *Node) findNode(key reflect.Value) *Node {
    81  	if n.Target == key {
    82  		return n
    83  	}
    84  	for _, child := range n.Children {
    85  		if found := child.findNode(key); found != nil {
    86  			return found
    87  		}
    88  	}
    89  	return nil
    90  }
    91  
    92  // AllFlags returns flags from all ancestor branches encountered.
    93  //
    94  // If "hide" is true hidden flags will be omitted.
    95  func (n *Node) AllFlags(hide bool) (out [][]*Flag) {
    96  	if n.Parent != nil {
    97  		out = append(out, n.Parent.AllFlags(hide)...)
    98  	}
    99  	group := []*Flag{}
   100  	for _, flag := range n.Flags {
   101  		if !hide || !flag.Hidden {
   102  			flag.Active = true
   103  			group = append(group, flag)
   104  		}
   105  	}
   106  	if len(group) > 0 {
   107  		out = append(out, group)
   108  	}
   109  	return
   110  }
   111  
   112  // Leaves returns the leaf commands/arguments under Node.
   113  //
   114  // If "hidden" is true hidden leaves will be omitted.
   115  func (n *Node) Leaves(hide bool) (out []*Node) {
   116  	_ = Visit(n, func(nd Visitable, next Next) error {
   117  		if nd == n {
   118  			return next(nil)
   119  		}
   120  		if node, ok := nd.(*Node); ok {
   121  			if hide && node.Hidden {
   122  				return nil
   123  			}
   124  			if len(node.Children) == 0 && node.Type != ApplicationNode {
   125  				out = append(out, node)
   126  			}
   127  		}
   128  		return next(nil)
   129  	})
   130  	return
   131  }
   132  
   133  // Depth of the command from the application root.
   134  func (n *Node) Depth() int {
   135  	depth := 0
   136  	p := n.Parent
   137  	for p != nil && p.Type != ApplicationNode {
   138  		depth++
   139  		p = p.Parent
   140  	}
   141  	return depth
   142  }
   143  
   144  // Summary help string for the node (not including application name).
   145  func (n *Node) Summary() string {
   146  	summary := n.Path()
   147  	if flags := n.FlagSummary(true); flags != "" {
   148  		summary += " " + flags
   149  	}
   150  	args := []string{}
   151  	optional := 0
   152  	for _, arg := range n.Positional {
   153  		argSummary := arg.Summary()
   154  		if arg.Tag.Optional {
   155  			optional++
   156  			argSummary = strings.TrimRight(argSummary, "]")
   157  		}
   158  		args = append(args, argSummary)
   159  	}
   160  	if len(args) != 0 {
   161  		summary += " " + strings.Join(args, " ") + strings.Repeat("]", optional)
   162  	} else if len(n.Children) > 0 {
   163  		summary += " <command>"
   164  	}
   165  	allFlags := n.Flags
   166  	if n.Parent != nil {
   167  		allFlags = append(allFlags, n.Parent.Flags...)
   168  	}
   169  	for _, flag := range allFlags {
   170  		if !flag.Required {
   171  			summary += " [flags]"
   172  			break
   173  		}
   174  	}
   175  	return summary
   176  }
   177  
   178  // FlagSummary for the node.
   179  func (n *Node) FlagSummary(hide bool) string {
   180  	required := []string{}
   181  	count := 0
   182  	for _, group := range n.AllFlags(hide) {
   183  		for _, flag := range group {
   184  			count++
   185  			if flag.Required {
   186  				required = append(required, flag.Summary())
   187  			}
   188  		}
   189  	}
   190  	return strings.Join(required, " ")
   191  }
   192  
   193  // FullPath is like Path() but includes the Application root node.
   194  func (n *Node) FullPath() string {
   195  	root := n
   196  	for root.Parent != nil {
   197  		root = root.Parent
   198  	}
   199  	return strings.TrimSpace(root.Name + " " + n.Path())
   200  }
   201  
   202  // Vars returns the combined Vars defined by all ancestors of this Node.
   203  func (n *Node) Vars() Vars {
   204  	if n == nil {
   205  		return Vars{}
   206  	}
   207  	return n.Parent.Vars().CloneWith(n.Tag.Vars)
   208  }
   209  
   210  // Path through ancestors to this Node.
   211  func (n *Node) Path() (out string) {
   212  	if n.Parent != nil {
   213  		out += " " + n.Parent.Path()
   214  	}
   215  	switch n.Type {
   216  	case CommandNode:
   217  		out += " " + n.Name
   218  		if len(n.Aliases) > 0 {
   219  			out += fmt.Sprintf(" (%s)", strings.Join(n.Aliases, ","))
   220  		}
   221  	case ArgumentNode:
   222  		out += " " + "<" + n.Name + ">"
   223  	default:
   224  	}
   225  	return strings.TrimSpace(out)
   226  }
   227  
   228  // ClosestGroup finds the first non-nil group in this node and its ancestors.
   229  func (n *Node) ClosestGroup() *Group {
   230  	switch {
   231  	case n.Group != nil:
   232  		return n.Group
   233  	case n.Parent != nil:
   234  		return n.Parent.ClosestGroup()
   235  	default:
   236  		return nil
   237  	}
   238  }
   239  
   240  // A Value is either a flag or a variable positional argument.
   241  type Value struct {
   242  	Flag         *Flag // Nil if positional argument.
   243  	Name         string
   244  	Help         string
   245  	OrigHelp     string // Original help string, without interpolated variables.
   246  	HasDefault   bool
   247  	Default      string
   248  	DefaultValue reflect.Value
   249  	Enum         string
   250  	Mapper       Mapper
   251  	Tag          *Tag
   252  	Target       reflect.Value
   253  	Required     bool
   254  	Set          bool   // Set to true when this value is set through some mechanism.
   255  	Format       string // Formatting directive, if applicable.
   256  	Position     int    // Position (for positional arguments).
   257  	Passthrough  bool   // Set to true to stop flag parsing when encountered.
   258  	Active       bool   // Denotes the value is part of an active branch in the CLI.
   259  }
   260  
   261  // EnumMap returns a map of the enums in this value.
   262  func (v *Value) EnumMap() map[string]bool {
   263  	parts := strings.Split(v.Enum, ",")
   264  	out := make(map[string]bool, len(parts))
   265  	for _, part := range parts {
   266  		out[strings.TrimSpace(part)] = true
   267  	}
   268  	return out
   269  }
   270  
   271  // EnumSlice returns a slice of the enums in this value.
   272  func (v *Value) EnumSlice() []string {
   273  	parts := strings.Split(v.Enum, ",")
   274  	out := make([]string, len(parts))
   275  	for i, part := range parts {
   276  		out[i] = strings.TrimSpace(part)
   277  	}
   278  	return out
   279  }
   280  
   281  // ShortSummary returns a human-readable summary of the value, not including any placeholders/defaults.
   282  func (v *Value) ShortSummary() string {
   283  	if v.Flag != nil {
   284  		return fmt.Sprintf("--%s", v.Name)
   285  	}
   286  	argText := "<" + v.Name + ">"
   287  	if v.IsCumulative() {
   288  		argText += " ..."
   289  	}
   290  	if !v.Required {
   291  		argText = "[" + argText + "]"
   292  	}
   293  	return argText
   294  }
   295  
   296  // Summary returns a human-readable summary of the value.
   297  func (v *Value) Summary() string {
   298  	if v.Flag != nil {
   299  		if v.IsBool() {
   300  			return fmt.Sprintf("--%s", v.Name)
   301  		}
   302  		return fmt.Sprintf("--%s=%s", v.Name, v.Flag.FormatPlaceHolder())
   303  	}
   304  	argText := "<" + v.Name + ">"
   305  	if v.IsCumulative() {
   306  		argText += " ..."
   307  	}
   308  	if !v.Required {
   309  		argText = "[" + argText + "]"
   310  	}
   311  	return argText
   312  }
   313  
   314  // IsCumulative returns true if the type can be accumulated into.
   315  func (v *Value) IsCumulative() bool {
   316  	return v.IsSlice() || v.IsMap()
   317  }
   318  
   319  // IsSlice returns true if the value is a slice.
   320  func (v *Value) IsSlice() bool {
   321  	return v.Target.Type().Name() == "" && v.Target.Kind() == reflect.Slice
   322  }
   323  
   324  // IsMap returns true if the value is a map.
   325  func (v *Value) IsMap() bool {
   326  	return v.Target.Kind() == reflect.Map
   327  }
   328  
   329  // IsBool returns true if the underlying value is a boolean.
   330  func (v *Value) IsBool() bool {
   331  	if m, ok := v.Mapper.(BoolMapperExt); ok && m.IsBoolFromValue(v.Target) {
   332  		return true
   333  	}
   334  	if m, ok := v.Mapper.(BoolMapper); ok && m.IsBool() {
   335  		return true
   336  	}
   337  	return v.Target.Kind() == reflect.Bool
   338  }
   339  
   340  // IsCounter returns true if the value is a counter.
   341  func (v *Value) IsCounter() bool {
   342  	return v.Tag.Type == "counter"
   343  }
   344  
   345  // Parse tokens into value, parse, and validate, but do not write to the field.
   346  func (v *Value) Parse(scan *Scanner, target reflect.Value) (err error) {
   347  	if target.Kind() == reflect.Ptr && target.IsNil() {
   348  		target.Set(reflect.New(target.Type().Elem()))
   349  	}
   350  	err = v.Mapper.Decode(&DecodeContext{Value: v, Scan: scan}, target)
   351  	if err != nil {
   352  		return fmt.Errorf("%s: %w", v.ShortSummary(), err)
   353  	}
   354  	v.Set = true
   355  	return nil
   356  }
   357  
   358  // Apply value to field.
   359  func (v *Value) Apply(value reflect.Value) {
   360  	v.Target.Set(value)
   361  	v.Set = true
   362  }
   363  
   364  // ApplyDefault value to field if it is not already set.
   365  func (v *Value) ApplyDefault() error {
   366  	if reflectValueIsZero(v.Target) {
   367  		return v.Reset()
   368  	}
   369  	v.Set = true
   370  	return nil
   371  }
   372  
   373  // Reset this value to its default, either the zero value or the parsed result of its envar,
   374  // or its "default" tag.
   375  //
   376  // Does not include resolvers.
   377  func (v *Value) Reset() error {
   378  	v.Target.Set(reflect.Zero(v.Target.Type()))
   379  	if len(v.Tag.Envs) != 0 {
   380  		for _, env := range v.Tag.Envs {
   381  			envar, ok := os.LookupEnv(env)
   382  			// Parse the first non-empty ENV in the list
   383  			if ok {
   384  				err := v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: envar}), v.Target)
   385  				if err != nil {
   386  					return fmt.Errorf("%s (from envar %s=%q)", err, env, envar)
   387  				}
   388  				return nil
   389  			}
   390  		}
   391  	}
   392  	if v.HasDefault {
   393  		return v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: v.Default}), v.Target)
   394  	}
   395  	return nil
   396  }
   397  
   398  func (*Value) node() {}
   399  
   400  // A Positional represents a non-branching command-line positional argument.
   401  type Positional = Value
   402  
   403  // A Flag represents a command-line flag.
   404  type Flag struct {
   405  	*Value
   406  	Group       *Group // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
   407  	Xor         []string
   408  	PlaceHolder string
   409  	Envs        []string
   410  	Aliases     []string
   411  	Short       rune
   412  	Hidden      bool
   413  	Negated     bool
   414  }
   415  
   416  func (f *Flag) String() string {
   417  	out := "--" + f.Name
   418  	if f.Short != 0 {
   419  		out = fmt.Sprintf("-%c, %s", f.Short, out)
   420  	}
   421  	if !f.IsBool() && !f.IsCounter() {
   422  		out += "=" + f.FormatPlaceHolder()
   423  	}
   424  	return out
   425  }
   426  
   427  // FormatPlaceHolder formats the placeholder string for a Flag.
   428  func (f *Flag) FormatPlaceHolder() string {
   429  	placeholderHelper, ok := f.Value.Mapper.(PlaceHolderProvider)
   430  	if ok {
   431  		return placeholderHelper.PlaceHolder(f)
   432  	}
   433  	tail := ""
   434  	if f.Value.IsSlice() && f.Value.Tag.Sep != -1 {
   435  		tail += string(f.Value.Tag.Sep) + "..."
   436  	}
   437  	if f.PlaceHolder != "" {
   438  		return f.PlaceHolder + tail
   439  	}
   440  	if f.HasDefault {
   441  		if f.Value.Target.Kind() == reflect.String {
   442  			return strconv.Quote(f.Default) + tail
   443  		}
   444  		return f.Default + tail
   445  	}
   446  	if f.Value.IsMap() {
   447  		if f.Value.Tag.MapSep != -1 {
   448  			tail = string(f.Value.Tag.MapSep) + "..."
   449  		}
   450  		return "KEY=VALUE" + tail
   451  	}
   452  	if f.Tag != nil && f.Tag.TypeName != "" {
   453  		return strings.ToUpper(dashedString(f.Tag.TypeName)) + tail
   454  	}
   455  	return strings.ToUpper(f.Name) + tail
   456  }
   457  
   458  // Group holds metadata about a command or flag group used when printing help.
   459  type Group struct {
   460  	// Key is the `group` field tag value used to identify this group.
   461  	Key string
   462  	// Title is displayed above the grouped items.
   463  	Title string
   464  	// Description is optional and displayed under the Title when non empty.
   465  	// It can be used to introduce the group's purpose to the user.
   466  	Description string
   467  }
   468  
   469  // This is directly from the Go 1.13 source code.
   470  func reflectValueIsZero(v reflect.Value) bool {
   471  	switch v.Kind() {
   472  	case reflect.Bool:
   473  		return !v.Bool()
   474  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   475  		return v.Int() == 0
   476  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   477  		return v.Uint() == 0
   478  	case reflect.Float32, reflect.Float64:
   479  		return math.Float64bits(v.Float()) == 0
   480  	case reflect.Complex64, reflect.Complex128:
   481  		c := v.Complex()
   482  		return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
   483  	case reflect.Array:
   484  		for i := 0; i < v.Len(); i++ {
   485  			if !reflectValueIsZero(v.Index(i)) {
   486  				return false
   487  			}
   488  		}
   489  		return true
   490  	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
   491  		return v.IsNil()
   492  	case reflect.String:
   493  		return v.Len() == 0
   494  	case reflect.Struct:
   495  		for i := 0; i < v.NumField(); i++ {
   496  			if !reflectValueIsZero(v.Field(i)) {
   497  				return false
   498  			}
   499  		}
   500  		return true
   501  	default:
   502  		// This should never happens, but will act as a safeguard for
   503  		// later, as a default value doesn't makes sense here.
   504  		panic(&reflect.ValueError{
   505  			Method: "reflect.Value.IsZero",
   506  			Kind:   v.Kind(),
   507  		})
   508  	}
   509  }