1package cli 2 3import ( 4 "flag" 5 "fmt" 6 "strconv" 7 "strings" 8) 9 10// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter 11type Int64Slice []int64 12 13// Set parses the value into an integer and appends it to the list of values 14func (f *Int64Slice) Set(value string) error { 15 tmp, err := strconv.ParseInt(value, 10, 64) 16 if err != nil { 17 return err 18 } 19 *f = append(*f, tmp) 20 return nil 21} 22 23// String returns a readable representation of this value (for usage defaults) 24func (f *Int64Slice) String() string { 25 slice := make([]string, len(*f)) 26 for i, v := range *f { 27 slice[i] = strconv.FormatInt(v, 10) 28 } 29 30 return strings.Join(slice, ",") 31} 32 33// Value returns the slice of ints set by this flag 34func (f *Int64Slice) Value() []int64 { 35 return *f 36} 37 38// Get returns the slice of ints set by this flag 39func (f *Int64Slice) Get() interface{} { 40 return *f 41} 42 43// Int64SliceFlag is a flag with type *Int64Slice 44type Int64SliceFlag struct { 45 Name string 46 Usage string 47 EnvVar string 48 FilePath string 49 Required bool 50 Hidden bool 51 Value *Int64Slice 52} 53 54// String returns a readable representation of this value 55// (for usage defaults) 56func (f Int64SliceFlag) String() string { 57 return FlagStringer(f) 58} 59 60// GetName returns the name of the flag 61func (f Int64SliceFlag) GetName() string { 62 return f.Name 63} 64 65// IsRequired returns whether or not the flag is required 66func (f Int64SliceFlag) IsRequired() bool { 67 return f.Required 68} 69 70// TakesValue returns true of the flag takes a value, otherwise false 71func (f Int64SliceFlag) TakesValue() bool { 72 return true 73} 74 75// GetUsage returns the usage string for the flag 76func (f Int64SliceFlag) GetUsage() string { 77 return f.Usage 78} 79 80// GetValue returns the flags value as string representation and an empty 81// string if the flag takes no value at all. 82func (f Int64SliceFlag) GetValue() string { 83 if f.Value != nil { 84 return f.Value.String() 85 } 86 return "" 87} 88 89// Apply populates the flag given the flag set and environment 90// Ignores errors 91func (f Int64SliceFlag) Apply(set *flag.FlagSet) { 92 _ = f.ApplyWithError(set) 93} 94 95// ApplyWithError populates the flag given the flag set and environment 96func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { 97 if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 98 newVal := &Int64Slice{} 99 for _, s := range strings.Split(envVal, ",") { 100 s = strings.TrimSpace(s) 101 if err := newVal.Set(s); err != nil { 102 return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) 103 } 104 } 105 if f.Value == nil { 106 f.Value = newVal 107 } else { 108 *f.Value = *newVal 109 } 110 } 111 112 eachName(f.Name, func(name string) { 113 if f.Value == nil { 114 f.Value = &Int64Slice{} 115 } 116 set.Var(f.Value, name, f.Usage) 117 }) 118 119 return nil 120} 121 122// Int64Slice looks up the value of a local Int64SliceFlag, returns 123// nil if not found 124func (c *Context) Int64Slice(name string) []int64 { 125 return lookupInt64Slice(name, c.flagSet) 126} 127 128// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns 129// nil if not found 130func (c *Context) GlobalInt64Slice(name string) []int64 { 131 if fs := lookupGlobalFlagSet(name, c); fs != nil { 132 return lookupInt64Slice(name, fs) 133 } 134 return nil 135} 136 137func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { 138 f := set.Lookup(name) 139 if f != nil { 140 value, ok := f.Value.(*Int64Slice) 141 if !ok { 142 return nil 143 } 144 145 // extract the slice from asserted value 146 parsed := value.Value() 147 148 // extract default value from the flag 149 var defaultVal []int64 150 for _, v := range strings.Split(f.DefValue, ",") { 151 if v != "" { 152 int64Value, err := strconv.ParseInt(v, 10, 64) 153 if err != nil { 154 panic(err) 155 } 156 defaultVal = append(defaultVal, int64Value) 157 } 158 } 159 // if the current value is not equal to the default value 160 // remove the default values from the flag 161 if !isInt64SliceEqual(parsed, defaultVal) { 162 for _, v := range defaultVal { 163 parsed = removeFromInt64Slice(parsed, v) 164 } 165 } 166 return parsed 167 } 168 return nil 169} 170 171func removeFromInt64Slice(slice []int64, val int64) []int64 { 172 for i, v := range slice { 173 if v == val { 174 ret := append([]int64{}, slice[:i]...) 175 ret = append(ret, slice[i+1:]...) 176 return ret 177 } 178 } 179 return slice 180} 181 182func isInt64SliceEqual(newValue, defaultValue []int64) bool { 183 // If one is nil, the other must also be nil. 184 if (newValue == nil) != (defaultValue == nil) { 185 return false 186 } 187 188 if len(newValue) != len(defaultValue) { 189 return false 190 } 191 192 for i, v := range newValue { 193 if v != defaultValue[i] { 194 return false 195 } 196 } 197 198 return true 199} 200