github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmdutil/flags.go (about)

     1  package cmdutil
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/spf13/cobra"
     9  	"github.com/spf13/pflag"
    10  )
    11  
    12  // NilStringFlag defines a new flag with a string pointer receiver. This is useful for differentiating
    13  // between the flag being set to a blank value and the flag not being passed at all.
    14  func NilStringFlag(cmd *cobra.Command, p **string, name string, shorthand string, usage string) *pflag.Flag {
    15  	return cmd.Flags().VarPF(newStringValue(p), name, shorthand, usage)
    16  }
    17  
    18  // NilBoolFlag defines a new flag with a bool pointer receiver. This is useful for differentiating
    19  // between the flag being explicitly set to a false value and the flag not being passed at all.
    20  func NilBoolFlag(cmd *cobra.Command, p **bool, name string, shorthand string, usage string) *pflag.Flag {
    21  	f := cmd.Flags().VarPF(newBoolValue(p), name, shorthand, usage)
    22  	f.NoOptDefVal = "true"
    23  	return f
    24  }
    25  
    26  // StringEnumFlag defines a new string flag that only allows values listed in options.
    27  func StringEnumFlag(cmd *cobra.Command, p *string, name, shorthand, defaultValue string, options []string, usage string) *pflag.Flag {
    28  	*p = defaultValue
    29  	val := &enumValue{string: p, options: options}
    30  	f := cmd.Flags().VarPF(val, name, shorthand, fmt.Sprintf("%s: %s", usage, formatValuesForUsageDocs(options)))
    31  	_ = cmd.RegisterFlagCompletionFunc(name, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    32  		return options, cobra.ShellCompDirectiveNoFileComp
    33  	})
    34  	return f
    35  }
    36  
    37  func StringSliceEnumFlag(cmd *cobra.Command, p *[]string, name, shorthand string, defaultValues, options []string, usage string) *pflag.Flag {
    38  	*p = defaultValues
    39  	val := &enumMultiValue{value: p, options: options}
    40  	f := cmd.Flags().VarPF(val, name, shorthand, fmt.Sprintf("%s: %s", usage, formatValuesForUsageDocs(options)))
    41  	_ = cmd.RegisterFlagCompletionFunc(name, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    42  		return options, cobra.ShellCompDirectiveNoFileComp
    43  	})
    44  	return f
    45  }
    46  
    47  func formatValuesForUsageDocs(values []string) string {
    48  	return fmt.Sprintf("{%s}", strings.Join(values, "|"))
    49  }
    50  
    51  type stringValue struct {
    52  	string **string
    53  }
    54  
    55  func newStringValue(p **string) *stringValue {
    56  	return &stringValue{p}
    57  }
    58  
    59  func (s *stringValue) Set(value string) error {
    60  	*s.string = &value
    61  	return nil
    62  }
    63  
    64  func (s *stringValue) String() string {
    65  	if s.string == nil || *s.string == nil {
    66  		return ""
    67  	}
    68  	return **s.string
    69  }
    70  
    71  func (s *stringValue) Type() string {
    72  	return "string"
    73  }
    74  
    75  type boolValue struct {
    76  	bool **bool
    77  }
    78  
    79  func newBoolValue(p **bool) *boolValue {
    80  	return &boolValue{p}
    81  }
    82  
    83  func (b *boolValue) Set(value string) error {
    84  	v, err := strconv.ParseBool(value)
    85  	*b.bool = &v
    86  	return err
    87  }
    88  
    89  func (b *boolValue) String() string {
    90  	if b.bool == nil || *b.bool == nil {
    91  		return "false"
    92  	} else if **b.bool {
    93  		return "true"
    94  	}
    95  	return "false"
    96  }
    97  
    98  func (b *boolValue) Type() string {
    99  	return "bool"
   100  }
   101  
   102  func (b *boolValue) IsBoolFlag() bool {
   103  	return true
   104  }
   105  
   106  type enumValue struct {
   107  	string  *string
   108  	options []string
   109  }
   110  
   111  func (e *enumValue) Set(value string) error {
   112  	if !isIncluded(value, e.options) {
   113  		return fmt.Errorf("valid values are %s", formatValuesForUsageDocs(e.options))
   114  	}
   115  	*e.string = value
   116  	return nil
   117  }
   118  
   119  func (e *enumValue) String() string {
   120  	return *e.string
   121  }
   122  
   123  func (e *enumValue) Type() string {
   124  	return "string"
   125  }
   126  
   127  type enumMultiValue struct {
   128  	value   *[]string
   129  	options []string
   130  }
   131  
   132  func (e *enumMultiValue) Set(value string) error {
   133  	items := strings.Split(value, ",")
   134  	for _, item := range items {
   135  		if !isIncluded(item, e.options) {
   136  			return fmt.Errorf("valid values are %s", formatValuesForUsageDocs(e.options))
   137  		}
   138  	}
   139  	*e.value = append(*e.value, items...)
   140  	return nil
   141  }
   142  
   143  func (e *enumMultiValue) String() string {
   144  	if len(*e.value) == 0 {
   145  		return ""
   146  	}
   147  	return fmt.Sprintf("{%s}", strings.Join(*e.value, ", "))
   148  }
   149  
   150  func (e *enumMultiValue) Type() string {
   151  	return "stringSlice"
   152  }
   153  
   154  func isIncluded(value string, opts []string) bool {
   155  	for _, opt := range opts {
   156  		if strings.EqualFold(opt, value) {
   157  			return true
   158  		}
   159  	}
   160  	return false
   161  }