You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

option.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. package flags
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "reflect"
  7. "strings"
  8. "unicode/utf8"
  9. )
  10. // Option flag information. Contains a description of the option, short and
  11. // long name as well as a default value and whether an argument for this
  12. // flag is optional.
  13. type Option struct {
  14. // The description of the option flag. This description is shown
  15. // automatically in the built-in help.
  16. Description string
  17. // The short name of the option (a single character). If not 0, the
  18. // option flag can be 'activated' using -<ShortName>. Either ShortName
  19. // or LongName needs to be non-empty.
  20. ShortName rune
  21. // The long name of the option. If not "", the option flag can be
  22. // activated using --<LongName>. Either ShortName or LongName needs
  23. // to be non-empty.
  24. LongName string
  25. // The default value of the option.
  26. Default []string
  27. // The optional environment default value key name.
  28. EnvDefaultKey string
  29. // The optional delimiter string for EnvDefaultKey values.
  30. EnvDefaultDelim string
  31. // If true, specifies that the argument to an option flag is optional.
  32. // When no argument to the flag is specified on the command line, the
  33. // value of OptionalValue will be set in the field this option represents.
  34. // This is only valid for non-boolean options.
  35. OptionalArgument bool
  36. // The optional value of the option. The optional value is used when
  37. // the option flag is marked as having an OptionalArgument. This means
  38. // that when the flag is specified, but no option argument is given,
  39. // the value of the field this option represents will be set to
  40. // OptionalValue. This is only valid for non-boolean options.
  41. OptionalValue []string
  42. // If true, the option _must_ be specified on the command line. If the
  43. // option is not specified, the parser will generate an ErrRequired type
  44. // error.
  45. Required bool
  46. // A name for the value of an option shown in the Help as --flag [ValueName]
  47. ValueName string
  48. // A mask value to show in the help instead of the default value. This
  49. // is useful for hiding sensitive information in the help, such as
  50. // passwords.
  51. DefaultMask string
  52. // If non empty, only a certain set of values is allowed for an option.
  53. Choices []string
  54. // If true, the option is not displayed in the help or man page
  55. Hidden bool
  56. // The group which the option belongs to
  57. group *Group
  58. // The struct field which the option represents.
  59. field reflect.StructField
  60. // The struct field value which the option represents.
  61. value reflect.Value
  62. // Determines if the option will be always quoted in the INI output
  63. iniQuote bool
  64. tag multiTag
  65. isSet bool
  66. isSetDefault bool
  67. preventDefault bool
  68. defaultLiteral string
  69. }
  70. // LongNameWithNamespace returns the option's long name with the group namespaces
  71. // prepended by walking up the option's group tree. Namespaces and the long name
  72. // itself are separated by the parser's namespace delimiter. If the long name is
  73. // empty an empty string is returned.
  74. func (option *Option) LongNameWithNamespace() string {
  75. if len(option.LongName) == 0 {
  76. return ""
  77. }
  78. // fetch the namespace delimiter from the parser which is always at the
  79. // end of the group hierarchy
  80. namespaceDelimiter := ""
  81. g := option.group
  82. for {
  83. if p, ok := g.parent.(*Parser); ok {
  84. namespaceDelimiter = p.NamespaceDelimiter
  85. break
  86. }
  87. switch i := g.parent.(type) {
  88. case *Command:
  89. g = i.Group
  90. case *Group:
  91. g = i
  92. }
  93. }
  94. // concatenate long name with namespace
  95. longName := option.LongName
  96. g = option.group
  97. for g != nil {
  98. if g.Namespace != "" {
  99. longName = g.Namespace + namespaceDelimiter + longName
  100. }
  101. switch i := g.parent.(type) {
  102. case *Command:
  103. g = i.Group
  104. case *Group:
  105. g = i
  106. case *Parser:
  107. g = nil
  108. }
  109. }
  110. return longName
  111. }
  112. // String converts an option to a human friendly readable string describing the
  113. // option.
  114. func (option *Option) String() string {
  115. var s string
  116. var short string
  117. if option.ShortName != 0 {
  118. data := make([]byte, utf8.RuneLen(option.ShortName))
  119. utf8.EncodeRune(data, option.ShortName)
  120. short = string(data)
  121. if len(option.LongName) != 0 {
  122. s = fmt.Sprintf("%s%s, %s%s",
  123. string(defaultShortOptDelimiter), short,
  124. defaultLongOptDelimiter, option.LongNameWithNamespace())
  125. } else {
  126. s = fmt.Sprintf("%s%s", string(defaultShortOptDelimiter), short)
  127. }
  128. } else if len(option.LongName) != 0 {
  129. s = fmt.Sprintf("%s%s", defaultLongOptDelimiter, option.LongNameWithNamespace())
  130. }
  131. return s
  132. }
  133. // Value returns the option value as an interface{}.
  134. func (option *Option) Value() interface{} {
  135. return option.value.Interface()
  136. }
  137. // Field returns the reflect struct field of the option.
  138. func (option *Option) Field() reflect.StructField {
  139. return option.field
  140. }
  141. // IsSet returns true if option has been set
  142. func (option *Option) IsSet() bool {
  143. return option.isSet
  144. }
  145. // IsSetDefault returns true if option has been set via the default option tag
  146. func (option *Option) IsSetDefault() bool {
  147. return option.isSetDefault
  148. }
  149. // Set the value of an option to the specified value. An error will be returned
  150. // if the specified value could not be converted to the corresponding option
  151. // value type.
  152. func (option *Option) set(value *string) error {
  153. kind := option.value.Type().Kind()
  154. if (kind == reflect.Map || kind == reflect.Slice) && !option.isSet {
  155. option.empty()
  156. }
  157. option.isSet = true
  158. option.preventDefault = true
  159. if len(option.Choices) != 0 {
  160. found := false
  161. for _, choice := range option.Choices {
  162. if choice == *value {
  163. found = true
  164. break
  165. }
  166. }
  167. if !found {
  168. allowed := strings.Join(option.Choices[0:len(option.Choices)-1], ", ")
  169. if len(option.Choices) > 1 {
  170. allowed += " or " + option.Choices[len(option.Choices)-1]
  171. }
  172. return newErrorf(ErrInvalidChoice,
  173. "Invalid value `%s' for option `%s'. Allowed values are: %s",
  174. *value, option, allowed)
  175. }
  176. }
  177. if option.isFunc() {
  178. return option.call(value)
  179. } else if value != nil {
  180. return convert(*value, option.value, option.tag)
  181. }
  182. return convert("", option.value, option.tag)
  183. }
  184. func (option *Option) canCli() bool {
  185. return option.ShortName != 0 || len(option.LongName) != 0
  186. }
  187. func (option *Option) canArgument() bool {
  188. if u := option.isUnmarshaler(); u != nil {
  189. return true
  190. }
  191. return !option.isBool()
  192. }
  193. func (option *Option) emptyValue() reflect.Value {
  194. tp := option.value.Type()
  195. if tp.Kind() == reflect.Map {
  196. return reflect.MakeMap(tp)
  197. }
  198. return reflect.Zero(tp)
  199. }
  200. func (option *Option) empty() {
  201. if !option.isFunc() {
  202. option.value.Set(option.emptyValue())
  203. }
  204. }
  205. func (option *Option) clearDefault() {
  206. usedDefault := option.Default
  207. if envKey := option.EnvDefaultKey; envKey != "" {
  208. if value, ok := os.LookupEnv(envKey); ok {
  209. if option.EnvDefaultDelim != "" {
  210. usedDefault = strings.Split(value,
  211. option.EnvDefaultDelim)
  212. } else {
  213. usedDefault = []string{value}
  214. }
  215. }
  216. }
  217. option.isSetDefault = true
  218. if len(usedDefault) > 0 {
  219. option.empty()
  220. for _, d := range usedDefault {
  221. option.set(&d)
  222. option.isSetDefault = true
  223. }
  224. } else {
  225. tp := option.value.Type()
  226. switch tp.Kind() {
  227. case reflect.Map:
  228. if option.value.IsNil() {
  229. option.empty()
  230. }
  231. case reflect.Slice:
  232. if option.value.IsNil() {
  233. option.empty()
  234. }
  235. }
  236. }
  237. }
  238. func (option *Option) valueIsDefault() bool {
  239. // Check if the value of the option corresponds to its
  240. // default value
  241. emptyval := option.emptyValue()
  242. checkvalptr := reflect.New(emptyval.Type())
  243. checkval := reflect.Indirect(checkvalptr)
  244. checkval.Set(emptyval)
  245. if len(option.Default) != 0 {
  246. for _, v := range option.Default {
  247. convert(v, checkval, option.tag)
  248. }
  249. }
  250. return reflect.DeepEqual(option.value.Interface(), checkval.Interface())
  251. }
  252. func (option *Option) isUnmarshaler() Unmarshaler {
  253. v := option.value
  254. for {
  255. if !v.CanInterface() {
  256. break
  257. }
  258. i := v.Interface()
  259. if u, ok := i.(Unmarshaler); ok {
  260. return u
  261. }
  262. if !v.CanAddr() {
  263. break
  264. }
  265. v = v.Addr()
  266. }
  267. return nil
  268. }
  269. func (option *Option) isBool() bool {
  270. tp := option.value.Type()
  271. for {
  272. switch tp.Kind() {
  273. case reflect.Slice, reflect.Ptr:
  274. tp = tp.Elem()
  275. case reflect.Bool:
  276. return true
  277. case reflect.Func:
  278. return tp.NumIn() == 0
  279. default:
  280. return false
  281. }
  282. }
  283. }
  284. func (option *Option) isSignedNumber() bool {
  285. tp := option.value.Type()
  286. for {
  287. switch tp.Kind() {
  288. case reflect.Slice, reflect.Ptr:
  289. tp = tp.Elem()
  290. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64:
  291. return true
  292. default:
  293. return false
  294. }
  295. }
  296. }
  297. func (option *Option) isFunc() bool {
  298. return option.value.Type().Kind() == reflect.Func
  299. }
  300. func (option *Option) call(value *string) error {
  301. var retval []reflect.Value
  302. if value == nil {
  303. retval = option.value.Call(nil)
  304. } else {
  305. tp := option.value.Type().In(0)
  306. val := reflect.New(tp)
  307. val = reflect.Indirect(val)
  308. if err := convert(*value, val, option.tag); err != nil {
  309. return err
  310. }
  311. retval = option.value.Call([]reflect.Value{val})
  312. }
  313. if len(retval) == 1 && retval[0].Type() == reflect.TypeOf((*error)(nil)).Elem() {
  314. if retval[0].Interface() == nil {
  315. return nil
  316. }
  317. return retval[0].Interface().(error)
  318. }
  319. return nil
  320. }
  321. func (option *Option) updateDefaultLiteral() {
  322. defs := option.Default
  323. def := ""
  324. if len(defs) == 0 && option.canArgument() {
  325. var showdef bool
  326. switch option.field.Type.Kind() {
  327. case reflect.Func, reflect.Ptr:
  328. showdef = !option.value.IsNil()
  329. case reflect.Slice, reflect.String, reflect.Array:
  330. showdef = option.value.Len() > 0
  331. case reflect.Map:
  332. showdef = !option.value.IsNil() && option.value.Len() > 0
  333. default:
  334. zeroval := reflect.Zero(option.field.Type)
  335. showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
  336. }
  337. if showdef {
  338. def, _ = convertToString(option.value, option.tag)
  339. }
  340. } else if len(defs) != 0 {
  341. l := len(defs) - 1
  342. for i := 0; i < l; i++ {
  343. def += quoteIfNeeded(defs[i]) + ", "
  344. }
  345. def += quoteIfNeeded(defs[l])
  346. }
  347. option.defaultLiteral = def
  348. }
  349. func (option *Option) shortAndLongName() string {
  350. ret := &bytes.Buffer{}
  351. if option.ShortName != 0 {
  352. ret.WriteRune(defaultShortOptDelimiter)
  353. ret.WriteRune(option.ShortName)
  354. }
  355. if len(option.LongName) != 0 {
  356. if option.ShortName != 0 {
  357. ret.WriteRune('/')
  358. }
  359. ret.WriteString(option.LongName)
  360. }
  361. return ret.String()
  362. }