⬆️ Update dependencies

This commit is contained in:
2026-11-01 15:12:52 +01:00
parent fd0d7ddee6
commit 312884dd59
501 changed files with 98424 additions and 35494 deletions
+155 -60
View File
@@ -7,8 +7,6 @@ import (
"reflect"
"strconv"
"strings"
"github.com/pkg/errors"
)
// A Visitable component in the model.
@@ -41,18 +39,22 @@ const (
// 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
Type NodeType
Parent *Node
Name string
Help string // Short help displayed in summaries.
Detail string // Detailed help displayed when describing command/arg alone.
Group *Group
Hidden bool
Flags []*Flag
Positional []*Positional
Children []*Node
DefaultCmd *Node
Target reflect.Value // Pointer to the value in the grammar that this Node is associated with.
Tag *Tag
Aliases []string
Passthrough bool // Set to true to stop flag parsing when encountered.
Active bool // Denotes the node is part of an active branch in the CLI.
Argument *Value // Populated when Type is ArgumentNode.
}
@@ -67,7 +69,7 @@ func (n *Node) Leaf() bool {
// 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 {
func (n *Node) Find(ptr any) *Node {
key := reflect.ValueOf(ptr)
if key.Kind() != reflect.Ptr {
panic("expected a pointer")
@@ -97,6 +99,7 @@ func (n *Node) AllFlags(hide bool) (out [][]*Flag) {
group := []*Flag{}
for _, flag := range n.Flags {
if !hide || !flag.Hidden {
flag.Active = true
group = append(group, flag)
}
}
@@ -127,6 +130,20 @@ func (n *Node) Leaves(hide bool) (out []*Node) {
return
}
// Values returns the values directly attached to this node: its argument
// value, flag values and positionals.
func (n *Node) Values() []*Value {
values := []*Value{}
if n.Argument != nil {
values = append(values, n.Argument)
}
for _, flag := range n.Flags {
values = append(values, flag.Value)
}
values = append(values, n.Positional...)
return values
}
// Depth of the command from the application root.
func (n *Node) Depth() int {
depth := 0
@@ -145,14 +162,33 @@ func (n *Node) Summary() string {
summary += " " + flags
}
args := []string{}
optional := 0
for _, arg := range n.Positional {
args = append(args, arg.Summary())
argSummary := arg.Summary()
if arg.Tag.Optional {
optional++
argSummary = strings.TrimRight(argSummary, "]")
}
args = append(args, argSummary)
}
if len(args) != 0 {
summary += " " + strings.Join(args, " ")
summary += " " + strings.Join(args, " ") + strings.Repeat("]", optional)
} else if len(n.Children) > 0 {
summary += " <command>"
}
allFlags := n.Flags
if n.Parent != nil {
allFlags = append(allFlags, n.Parent.Flags...)
}
for _, flag := range allFlags {
if _, ok := flag.Target.Interface().(helpFlag); ok {
continue
}
if !flag.Required {
summary += " [flags]"
break
}
}
return summary
}
@@ -196,27 +232,48 @@ func (n *Node) Path() (out string) {
switch n.Type {
case CommandNode:
out += " " + n.Name
if len(n.Aliases) > 0 {
out += fmt.Sprintf(" (%s)", strings.Join(n.Aliases, ","))
}
case ArgumentNode:
out += " " + "<" + n.Name + ">"
default:
}
return strings.TrimSpace(out)
}
// ClosestGroup finds the first non-nil group in this node and its ancestors.
func (n *Node) ClosestGroup() *Group {
switch {
case n.Group != nil:
return n.Group
case n.Parent != nil:
return n.Parent.ClosestGroup()
default:
return nil
}
}
// 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).
Flag *Flag // Nil if positional argument.
Name string
Help string
OrigHelp string // Original help string, without interpolated variables.
HasDefault bool
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).
Passthrough bool // Deprecated: Use PassthroughMode instead. Set to true to stop flag parsing when encountered.
PassthroughMode PassthroughMode //
Active bool // Denotes the value is part of an active branch in the CLI.
}
// EnumMap returns a map of the enums in this value.
@@ -229,6 +286,16 @@ func (v *Value) EnumMap() map[string]bool {
return out
}
// EnumSlice returns a slice of the enums in this value.
func (v *Value) EnumSlice() []string {
parts := strings.Split(v.Enum, ",")
out := make([]string, len(parts))
for i, part := range parts {
out[i] = strings.TrimSpace(part)
}
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 {
@@ -279,27 +346,28 @@ func (v *Value) IsMap() bool {
// IsBool returns true if the underlying value is a boolean.
func (v *Value) IsBool() bool {
if m, ok := v.Mapper.(BoolMapperExt); ok && m.IsBoolFromValue(v.Target) {
return true
}
if m, ok := v.Mapper.(BoolMapper); ok && m.IsBool() {
return true
}
return v.Target.Kind() == reflect.Bool
}
// IsCounter returns true if the value is a counter.
func (v *Value) IsCounter() bool {
return v.Tag.Type == "counter"
}
// 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))
}
}
}()
if target.Kind() == reflect.Ptr && target.IsNil() {
target.Set(reflect.New(target.Type().Elem()))
}
err = v.Mapper.Decode(&DecodeContext{Value: v, Scan: scan}, target)
if err != nil {
return errors.Wrap(err, v.ShortSummary())
return fmt.Errorf("%s: %w", v.ShortSummary(), err)
}
v.Set = true
return nil
@@ -326,17 +394,20 @@ func (v *Value) ApplyDefault() error {
// 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)
if len(v.Tag.Envs) != 0 {
for _, env := range v.Tag.Envs {
envar, ok := os.LookupEnv(env)
// Parse the first non-empty ENV in the list
if ok {
err := v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: envar}), v.Target)
if err != nil {
return fmt.Errorf("%s (from envar %s=%q)", err, env, envar)
}
return nil
}
return nil
}
}
if v.Default != "" {
if v.HasDefault {
return v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: v.Default}), v.Target)
}
return nil
@@ -350,12 +421,15 @@ 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
Group *Group // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
Xor []string
And []string
PlaceHolder string
Env string
Envs []string
Aliases []string
Short rune
Hidden bool
Negated bool
}
func (f *Flag) String() string {
@@ -363,7 +437,7 @@ func (f *Flag) String() string {
if f.Short != 0 {
out = fmt.Sprintf("-%c, %s", f.Short, out)
}
if !f.IsBool() {
if !f.IsBool() && !f.IsCounter() {
out += "=" + f.FormatPlaceHolder()
}
return out
@@ -371,28 +445,46 @@ func (f *Flag) String() string {
// FormatPlaceHolder formats the placeholder string for a Flag.
func (f *Flag) FormatPlaceHolder() string {
placeholderHelper, ok := f.Value.Mapper.(PlaceHolderProvider)
if ok {
return placeholderHelper.PlaceHolder(f)
}
tail := ""
if f.Value.IsSlice() && f.Value.Tag.Sep != -1 {
if f.Value.IsSlice() && f.Value.Tag.Sep != -1 && f.Tag.Type == "" {
tail += string(f.Value.Tag.Sep) + "..."
}
if f.Default != "" {
if f.PlaceHolder != "" {
return f.PlaceHolder + tail
}
if f.HasDefault {
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 {
if f.Value.Tag.MapSep != -1 && f.Tag.Type == "" {
tail = string(f.Value.Tag.MapSep) + "..."
}
return "KEY=VALUE" + tail
}
if f.Tag != nil && f.Tag.TypeName != "" {
return strings.ToUpper(dashedString(f.Tag.TypeName)) + tail
}
return strings.ToUpper(f.Name) + tail
}
// Group holds metadata about a command or flag group used when printing help.
type Group struct {
// Key is the `group` field tag value used to identify this group.
Key string
// Title is displayed above the grouped items.
Title string
// Description is optional and displayed under the Title when non empty.
// It can be used to introduce the group's purpose to the user.
Description string
}
// This is directly from the Go 1.13 source code.
func reflectValueIsZero(v reflect.Value) bool {
switch v.Kind() {
@@ -428,6 +520,9 @@ func reflectValueIsZero(v reflect.Value) bool {
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()})
panic(&reflect.ValueError{
Method: "reflect.Value.IsZero",
Kind: v.Kind(),
})
}
}