1package cbflag 2 3import ( 4 "fmt" 5 "strings" 6 7 "github.com/couchbase/cbflag/pwd" 8) 9 10// | TOTAL_LEN (80) | 11// | PREFIX_LEN (2) | FLAGS_LEN (25) | POSTFIX_LEN (3) | USAGE_LEN (50) | 12// | PREFIX_LEN (2) | flags definition | padding | POSTFIX_LEN (3) | USAGE_LEN (50) | 13 14const PREFIX_LEN = 2 15const POSTFIX_LEN = 3 16const FLAGS_LEN int = 25 17const USAGE_LEN int = 50 18const TOTAL_LEN int = 80 19 20type ValidatorFn func(Value) error 21type OptionHandler func(string, string) (string, bool, error) 22 23type Flag struct { 24 short string 25 long string 26 env string 27 deprecated []string 28 desc string 29 value Value 30 validator ValidatorFn 31 optHandler OptionHandler 32 foundLong bool 33 foundShort bool 34 foundEnv bool 35 foundDepr bool 36 required bool 37 hidden bool 38 isFlag bool 39} 40 41func BoolFlag(result *bool, def bool, short, long, env, usage string, deprecated []string, hidden bool) *Flag { 42 return varFlag(newBoolValue(def, result), short, long, env, usage, deprecated, nil, 43 DefaultOptionHandler, false, hidden, true) 44} 45 46func Float64Flag(result *float64, def float64, short, long, env, usage string, deprecated []string, 47 validator ValidatorFn, required, hidden bool) *Flag { 48 return varFlag(newFloat64Value(def, result), short, long, env, usage, deprecated, validator, 49 DefaultOptionHandler, required, hidden, false) 50} 51 52func IntFlag(result *int, def int, short, long, env, usage string, deprecated []string, validator ValidatorFn, 53 required, hidden bool) *Flag { 54 return varFlag(newIntValue(def, result), short, long, env, usage, deprecated, validator, 55 DefaultOptionHandler, required, hidden, false) 56} 57 58func IntArrayFlag(result *[]int, def []int, short, long, env, usage string, deprecated []string, validator ValidatorFn, 59 required, hidden bool) *Flag { 60 return varFlag(newIntArray(def, result), short, long, env, usage, deprecated, validator, 61 DefaultOptionHandler, required, hidden, false) 62} 63 64func Int64Flag(result *int64, def int64, short, long, env, usage string, deprecated []string, 65 validator ValidatorFn, required, hidden bool) *Flag { 66 return varFlag(newInt64Value(def, result), short, long, env, usage, deprecated, validator, 67 DefaultOptionHandler, required, hidden, false) 68} 69 70func RuneFlag(result *rune, def rune, short, long, env, usage string, deprecated []string, 71 validator ValidatorFn, required, hidden bool) *Flag { 72 return varFlag(newRuneValue(def, result), short, long, env, usage, deprecated, validator, 73 DefaultOptionHandler, required, hidden, false) 74} 75 76func StringFlag(result *string, def, short, long, env, usage string, deprecated []string, 77 validator ValidatorFn, required, hidden bool) *Flag { 78 return varFlag(newStringValue(def, result), short, long, env, usage, deprecated, validator, 79 DefaultOptionHandler, required, hidden, false) 80} 81 82func StringMapFlag(result *map[string]string, def map[string]string, short, long, env, usage string, 83 deprecated []string, validator ValidatorFn, required, hidden bool) *Flag { 84 return varFlag(newStringMapValue(def, result), short, long, env, usage, deprecated, validator, 85 DefaultOptionHandler, required, hidden, false) 86} 87 88func UintFlag(result *uint, def uint, short, long, env, usage string, deprecated []string, 89 validator ValidatorFn, required, hidden bool) *Flag { 90 return varFlag(newUintValue(def, result), short, long, env, usage, deprecated, validator, 91 DefaultOptionHandler, required, hidden, false) 92} 93 94func Uint64Flag(result *uint64, def uint64, short, long, env, usage string, deprecated []string, 95 validator ValidatorFn, required, hidden bool) *Flag { 96 return varFlag(newUint64Value(def, result), short, long, env, usage, deprecated, validator, 97 DefaultOptionHandler, required, hidden, false) 98} 99 100func HostFlag(result *string, def string, deprecated []string, required, hidden bool) *Flag { 101 return varFlag(newStringValue(def, result), "c", "cluster", "CB_CLUSTER", 102 "The hostname of the Couchbase cluster", deprecated, HostValidator, DefaultOptionHandler, 103 required, hidden, false) 104} 105 106func UsernameFlag(result *string, def string, deprecated []string, required, hidden bool) *Flag { 107 return varFlag(newStringValue(def, result), "u", "username", "CB_USERNAME", 108 "The username of the Couchbase cluster", deprecated, nil, DefaultOptionHandler, required, 109 hidden, false) 110} 111 112func PasswordFlag(result *string, def string, deprecated []string, required, hidden bool) *Flag { 113 return varFlag(newStringValue(def, result), "p", "password", "CB_PASSWORD", 114 "The password of the Couchbase cluster", deprecated, nil, PasswordOptionHandler, required, 115 hidden, false) 116} 117 118func CACertFlag(result *string, def string, deprecated []string, required, hidden bool) *Flag { 119 return varFlag(newStringValue(def, result), "", "cacert", "", 120 "Verifies the cluster identity with this certificate", deprecated, nil, DefaultOptionHandler, 121 required, hidden, false) 122} 123 124func NoSSLVerifyFlag(result *bool, deprecated []string, required, hidden bool) *Flag { 125 return varFlag(newBoolValue(false, result), "", "no-ssl-verify", "", 126 "Skips SSL verification of certificates against CA", deprecated, nil, DefaultOptionHandler, 127 required, hidden, true) 128} 129 130func helpFlag(result *bool) *Flag { 131 return varFlag(newBoolValue(false, result), "h", "help", "", "Prints the help message", 132 make([]string, 0), nil, DefaultOptionHandler, false, false, true) 133} 134 135func varFlag(value Value, short, long, env, usage string, deprecated []string, validator ValidatorFn, 136 optHandler OptionHandler, required, hidden, isFlag bool) *Flag { 137 return &Flag{ 138 short: short, 139 long: long, 140 env: env, 141 deprecated: deprecated, 142 desc: usage, 143 value: value, 144 validator: validator, 145 optHandler: optHandler, 146 foundLong: false, 147 foundShort: false, 148 foundEnv: false, 149 foundDepr: false, 150 required: required, 151 hidden: hidden, 152 isFlag: isFlag, 153 } 154} 155 156func (f *Flag) found() bool { 157 return f.foundLong || f.foundShort || f.foundEnv || f.foundDepr 158} 159 160func (f *Flag) foundNonEnv() bool { 161 return f.foundLong || f.foundShort || f.foundDepr 162} 163 164func (f *Flag) deprecatedFlagSpecified() bool { 165 return f.foundDepr 166} 167 168func (f *Flag) markFound(value string, environment, deprecated bool) { 169 if deprecated { 170 f.foundDepr = true 171 } else if environment { 172 f.foundEnv = true 173 } else if strings.HasPrefix(value, "--") { 174 f.foundLong = true 175 } else if strings.HasPrefix(value, "-") { 176 f.foundShort = true 177 } 178} 179 180func (f *Flag) validate() error { 181 if f.validator == nil { 182 return nil 183 } 184 185 return f.validator(f.value) 186} 187 188func (f *Flag) usageString() string { 189 if f.hidden { 190 return "" 191 } 192 193 s := "" 194 lines := f.splitDescription() 195 196 flagsStr := f.flagsHelpString() 197 if len(f.long) > FLAGS_LEN { 198 prePadding := strings.Repeat(" ", PREFIX_LEN) 199 s += fmt.Sprintf("%s%s\n", prePadding, flagsStr) 200 s += fmt.Sprintf("%s%s\n", strings.Repeat(" ", TOTAL_LEN-USAGE_LEN), lines[0]) 201 } else { 202 prePadding := strings.Repeat(" ", PREFIX_LEN) 203 postPadding := strings.Repeat(" ", FLAGS_LEN+POSTFIX_LEN-len(flagsStr)) 204 s += fmt.Sprintf("%s%s%s%s\n", prePadding, flagsStr, postPadding, lines[0]) 205 } 206 207 for i := 1; i < len(lines); i++ { 208 s += fmt.Sprintf("%s%s\n", strings.Repeat(" ", TOTAL_LEN-USAGE_LEN), lines[i]) 209 } 210 211 return s 212} 213 214func (f *Flag) flagsHelpString() string { 215 if f.short != "" && f.long != "" { 216 return fmt.Sprintf("-%s,--%s", f.short, f.long) 217 } else if f.short == "" { 218 return fmt.Sprintf(" --%s", f.long) 219 } else if f.long == "" { 220 return fmt.Sprintf("-%s", f.short) 221 } else { 222 return "" 223 } 224} 225 226func (f *Flag) deprecatedFlagsString() string { 227 rv := "" 228 for _, depr := range f.deprecated { 229 if len(rv) > 0 { 230 rv += "," 231 } 232 233 if len(depr) == 1 { 234 rv += "-" + depr 235 } else { 236 rv += "--" + depr 237 } 238 } 239 240 return rv 241} 242 243func (f *Flag) splitDescription() []string { 244 desc := f.desc 245 lines := make([]string, 0) 246 for len(desc) > 50 { 247 i := 50 248 for ; i >= 0; i-- { 249 if desc[i] == ' ' { 250 break 251 } 252 } 253 lines = append(lines, desc[0:i]) 254 desc = desc[i+1:] 255 } 256 257 return append(lines, desc) 258} 259 260func DefaultOptionHandler(opt, value string) (string, bool, error) { 261 if value == "" || strings.HasPrefix(value, "-") { 262 return value, false, fmt.Errorf("Expected argument for option: %s", opt) 263 } 264 265 return value, true, nil 266} 267 268func PasswordOptionHandler(opt, value string) (string, bool, error) { 269 if value == "" || strings.HasPrefix(value, "-") { 270 fmt.Print("Password: ") 271 password, err := pwd.GetPasswd() 272 return string(password), false, err 273 } 274 275 return value, true, nil 276} 277