flag.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. package cli
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "reflect"
  7. "runtime"
  8. "strconv"
  9. "strings"
  10. "time"
  11. )
  12. const defaultPlaceholder = "value"
  13. // BashCompletionFlag enables bash-completion for all commands and subcommands
  14. var BashCompletionFlag = BoolFlag{
  15. Name: "generate-bash-completion",
  16. Hidden: true,
  17. }
  18. // VersionFlag prints the version for the application
  19. var VersionFlag = BoolFlag{
  20. Name: "version, v",
  21. Usage: "print the version",
  22. }
  23. // HelpFlag prints the help for all commands and subcommands
  24. // Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
  25. // unless HideHelp is set to true)
  26. var HelpFlag = BoolFlag{
  27. Name: "help, h",
  28. Usage: "show help",
  29. }
  30. // FlagStringer converts a flag definition to a string. This is used by help
  31. // to display a flag.
  32. var FlagStringer FlagStringFunc = stringifyFlag
  33. // Flag is a common interface related to parsing flags in cli.
  34. // For more advanced flag parsing techniques, it is recommended that
  35. // this interface be implemented.
  36. type Flag interface {
  37. fmt.Stringer
  38. // Apply Flag settings to the given flag set
  39. Apply(*flag.FlagSet)
  40. GetName() string
  41. }
  42. func flagSet(name string, flags []Flag) *flag.FlagSet {
  43. set := flag.NewFlagSet(name, flag.ContinueOnError)
  44. for _, f := range flags {
  45. f.Apply(set)
  46. }
  47. return set
  48. }
  49. func eachName(longName string, fn func(string)) {
  50. parts := strings.Split(longName, ",")
  51. for _, name := range parts {
  52. name = strings.Trim(name, " ")
  53. fn(name)
  54. }
  55. }
  56. // Generic is a generic parseable type identified by a specific flag
  57. type Generic interface {
  58. Set(value string) error
  59. String() string
  60. }
  61. // GenericFlag is the flag type for types implementing Generic
  62. type GenericFlag struct {
  63. Name string
  64. Value Generic
  65. Usage string
  66. EnvVar string
  67. Hidden bool
  68. }
  69. // String returns the string representation of the generic flag to display the
  70. // help text to the user (uses the String() method of the generic flag to show
  71. // the value)
  72. func (f GenericFlag) String() string {
  73. return FlagStringer(f)
  74. }
  75. // Apply takes the flagset and calls Set on the generic flag with the value
  76. // provided by the user for parsing by the flag
  77. func (f GenericFlag) Apply(set *flag.FlagSet) {
  78. val := f.Value
  79. if f.EnvVar != "" {
  80. for _, envVar := range strings.Split(f.EnvVar, ",") {
  81. envVar = strings.TrimSpace(envVar)
  82. if envVal := os.Getenv(envVar); envVal != "" {
  83. val.Set(envVal)
  84. break
  85. }
  86. }
  87. }
  88. eachName(f.Name, func(name string) {
  89. set.Var(f.Value, name, f.Usage)
  90. })
  91. }
  92. // GetName returns the name of a flag.
  93. func (f GenericFlag) GetName() string {
  94. return f.Name
  95. }
  96. // StringSlice is an opaque type for []string to satisfy flag.Value
  97. type StringSlice []string
  98. // Set appends the string value to the list of values
  99. func (f *StringSlice) Set(value string) error {
  100. *f = append(*f, value)
  101. return nil
  102. }
  103. // String returns a readable representation of this value (for usage defaults)
  104. func (f *StringSlice) String() string {
  105. return fmt.Sprintf("%s", *f)
  106. }
  107. // Value returns the slice of strings set by this flag
  108. func (f *StringSlice) Value() []string {
  109. return *f
  110. }
  111. // StringSliceFlag is a string flag that can be specified multiple times on the
  112. // command-line
  113. type StringSliceFlag struct {
  114. Name string
  115. Value *StringSlice
  116. Usage string
  117. EnvVar string
  118. Hidden bool
  119. }
  120. // String returns the usage
  121. func (f StringSliceFlag) String() string {
  122. return FlagStringer(f)
  123. }
  124. // Apply populates the flag given the flag set and environment
  125. func (f StringSliceFlag) Apply(set *flag.FlagSet) {
  126. if f.EnvVar != "" {
  127. for _, envVar := range strings.Split(f.EnvVar, ",") {
  128. envVar = strings.TrimSpace(envVar)
  129. if envVal := os.Getenv(envVar); envVal != "" {
  130. newVal := &StringSlice{}
  131. for _, s := range strings.Split(envVal, ",") {
  132. s = strings.TrimSpace(s)
  133. newVal.Set(s)
  134. }
  135. f.Value = newVal
  136. break
  137. }
  138. }
  139. }
  140. eachName(f.Name, func(name string) {
  141. if f.Value == nil {
  142. f.Value = &StringSlice{}
  143. }
  144. set.Var(f.Value, name, f.Usage)
  145. })
  146. }
  147. // GetName returns the name of a flag.
  148. func (f StringSliceFlag) GetName() string {
  149. return f.Name
  150. }
  151. // IntSlice is an opaque type for []int to satisfy flag.Value
  152. type IntSlice []int
  153. // Set parses the value into an integer and appends it to the list of values
  154. func (f *IntSlice) Set(value string) error {
  155. tmp, err := strconv.Atoi(value)
  156. if err != nil {
  157. return err
  158. }
  159. *f = append(*f, tmp)
  160. return nil
  161. }
  162. // String returns a readable representation of this value (for usage defaults)
  163. func (f *IntSlice) String() string {
  164. return fmt.Sprintf("%d", *f)
  165. }
  166. // Value returns the slice of ints set by this flag
  167. func (f *IntSlice) Value() []int {
  168. return *f
  169. }
  170. // IntSliceFlag is an int flag that can be specified multiple times on the
  171. // command-line
  172. type IntSliceFlag struct {
  173. Name string
  174. Value *IntSlice
  175. Usage string
  176. EnvVar string
  177. Hidden bool
  178. }
  179. // String returns the usage
  180. func (f IntSliceFlag) String() string {
  181. return FlagStringer(f)
  182. }
  183. // Apply populates the flag given the flag set and environment
  184. func (f IntSliceFlag) Apply(set *flag.FlagSet) {
  185. if f.EnvVar != "" {
  186. for _, envVar := range strings.Split(f.EnvVar, ",") {
  187. envVar = strings.TrimSpace(envVar)
  188. if envVal := os.Getenv(envVar); envVal != "" {
  189. newVal := &IntSlice{}
  190. for _, s := range strings.Split(envVal, ",") {
  191. s = strings.TrimSpace(s)
  192. err := newVal.Set(s)
  193. if err != nil {
  194. fmt.Fprintf(ErrWriter, err.Error())
  195. }
  196. }
  197. f.Value = newVal
  198. break
  199. }
  200. }
  201. }
  202. eachName(f.Name, func(name string) {
  203. if f.Value == nil {
  204. f.Value = &IntSlice{}
  205. }
  206. set.Var(f.Value, name, f.Usage)
  207. })
  208. }
  209. // GetName returns the name of the flag.
  210. func (f IntSliceFlag) GetName() string {
  211. return f.Name
  212. }
  213. // BoolFlag is a switch that defaults to false
  214. type BoolFlag struct {
  215. Name string
  216. Usage string
  217. EnvVar string
  218. Destination *bool
  219. Hidden bool
  220. }
  221. // String returns a readable representation of this value (for usage defaults)
  222. func (f BoolFlag) String() string {
  223. return FlagStringer(f)
  224. }
  225. // Apply populates the flag given the flag set and environment
  226. func (f BoolFlag) Apply(set *flag.FlagSet) {
  227. val := false
  228. if f.EnvVar != "" {
  229. for _, envVar := range strings.Split(f.EnvVar, ",") {
  230. envVar = strings.TrimSpace(envVar)
  231. if envVal := os.Getenv(envVar); envVal != "" {
  232. envValBool, err := strconv.ParseBool(envVal)
  233. if err == nil {
  234. val = envValBool
  235. }
  236. break
  237. }
  238. }
  239. }
  240. eachName(f.Name, func(name string) {
  241. if f.Destination != nil {
  242. set.BoolVar(f.Destination, name, val, f.Usage)
  243. return
  244. }
  245. set.Bool(name, val, f.Usage)
  246. })
  247. }
  248. // GetName returns the name of the flag.
  249. func (f BoolFlag) GetName() string {
  250. return f.Name
  251. }
  252. // BoolTFlag this represents a boolean flag that is true by default, but can
  253. // still be set to false by --some-flag=false
  254. type BoolTFlag struct {
  255. Name string
  256. Usage string
  257. EnvVar string
  258. Destination *bool
  259. Hidden bool
  260. }
  261. // String returns a readable representation of this value (for usage defaults)
  262. func (f BoolTFlag) String() string {
  263. return FlagStringer(f)
  264. }
  265. // Apply populates the flag given the flag set and environment
  266. func (f BoolTFlag) Apply(set *flag.FlagSet) {
  267. val := true
  268. if f.EnvVar != "" {
  269. for _, envVar := range strings.Split(f.EnvVar, ",") {
  270. envVar = strings.TrimSpace(envVar)
  271. if envVal := os.Getenv(envVar); envVal != "" {
  272. envValBool, err := strconv.ParseBool(envVal)
  273. if err == nil {
  274. val = envValBool
  275. break
  276. }
  277. }
  278. }
  279. }
  280. eachName(f.Name, func(name string) {
  281. if f.Destination != nil {
  282. set.BoolVar(f.Destination, name, val, f.Usage)
  283. return
  284. }
  285. set.Bool(name, val, f.Usage)
  286. })
  287. }
  288. // GetName returns the name of the flag.
  289. func (f BoolTFlag) GetName() string {
  290. return f.Name
  291. }
  292. // StringFlag represents a flag that takes as string value
  293. type StringFlag struct {
  294. Name string
  295. Value string
  296. Usage string
  297. EnvVar string
  298. Destination *string
  299. Hidden bool
  300. }
  301. // String returns the usage
  302. func (f StringFlag) String() string {
  303. return FlagStringer(f)
  304. }
  305. // Apply populates the flag given the flag set and environment
  306. func (f StringFlag) Apply(set *flag.FlagSet) {
  307. if f.EnvVar != "" {
  308. for _, envVar := range strings.Split(f.EnvVar, ",") {
  309. envVar = strings.TrimSpace(envVar)
  310. if envVal := os.Getenv(envVar); envVal != "" {
  311. f.Value = envVal
  312. break
  313. }
  314. }
  315. }
  316. eachName(f.Name, func(name string) {
  317. if f.Destination != nil {
  318. set.StringVar(f.Destination, name, f.Value, f.Usage)
  319. return
  320. }
  321. set.String(name, f.Value, f.Usage)
  322. })
  323. }
  324. // GetName returns the name of the flag.
  325. func (f StringFlag) GetName() string {
  326. return f.Name
  327. }
  328. // IntFlag is a flag that takes an integer
  329. // Errors if the value provided cannot be parsed
  330. type IntFlag struct {
  331. Name string
  332. Value int
  333. Usage string
  334. EnvVar string
  335. Destination *int
  336. Hidden bool
  337. }
  338. // String returns the usage
  339. func (f IntFlag) String() string {
  340. return FlagStringer(f)
  341. }
  342. // Apply populates the flag given the flag set and environment
  343. func (f IntFlag) Apply(set *flag.FlagSet) {
  344. if f.EnvVar != "" {
  345. for _, envVar := range strings.Split(f.EnvVar, ",") {
  346. envVar = strings.TrimSpace(envVar)
  347. if envVal := os.Getenv(envVar); envVal != "" {
  348. envValInt, err := strconv.ParseInt(envVal, 0, 64)
  349. if err == nil {
  350. f.Value = int(envValInt)
  351. break
  352. }
  353. }
  354. }
  355. }
  356. eachName(f.Name, func(name string) {
  357. if f.Destination != nil {
  358. set.IntVar(f.Destination, name, f.Value, f.Usage)
  359. return
  360. }
  361. set.Int(name, f.Value, f.Usage)
  362. })
  363. }
  364. // GetName returns the name of the flag.
  365. func (f IntFlag) GetName() string {
  366. return f.Name
  367. }
  368. // DurationFlag is a flag that takes a duration specified in Go's duration
  369. // format: https://golang.org/pkg/time/#ParseDuration
  370. type DurationFlag struct {
  371. Name string
  372. Value time.Duration
  373. Usage string
  374. EnvVar string
  375. Destination *time.Duration
  376. Hidden bool
  377. }
  378. // String returns a readable representation of this value (for usage defaults)
  379. func (f DurationFlag) String() string {
  380. return FlagStringer(f)
  381. }
  382. // Apply populates the flag given the flag set and environment
  383. func (f DurationFlag) Apply(set *flag.FlagSet) {
  384. if f.EnvVar != "" {
  385. for _, envVar := range strings.Split(f.EnvVar, ",") {
  386. envVar = strings.TrimSpace(envVar)
  387. if envVal := os.Getenv(envVar); envVal != "" {
  388. envValDuration, err := time.ParseDuration(envVal)
  389. if err == nil {
  390. f.Value = envValDuration
  391. break
  392. }
  393. }
  394. }
  395. }
  396. eachName(f.Name, func(name string) {
  397. if f.Destination != nil {
  398. set.DurationVar(f.Destination, name, f.Value, f.Usage)
  399. return
  400. }
  401. set.Duration(name, f.Value, f.Usage)
  402. })
  403. }
  404. // GetName returns the name of the flag.
  405. func (f DurationFlag) GetName() string {
  406. return f.Name
  407. }
  408. // Float64Flag is a flag that takes an float value
  409. // Errors if the value provided cannot be parsed
  410. type Float64Flag struct {
  411. Name string
  412. Value float64
  413. Usage string
  414. EnvVar string
  415. Destination *float64
  416. Hidden bool
  417. }
  418. // String returns the usage
  419. func (f Float64Flag) String() string {
  420. return FlagStringer(f)
  421. }
  422. // Apply populates the flag given the flag set and environment
  423. func (f Float64Flag) Apply(set *flag.FlagSet) {
  424. if f.EnvVar != "" {
  425. for _, envVar := range strings.Split(f.EnvVar, ",") {
  426. envVar = strings.TrimSpace(envVar)
  427. if envVal := os.Getenv(envVar); envVal != "" {
  428. envValFloat, err := strconv.ParseFloat(envVal, 10)
  429. if err == nil {
  430. f.Value = float64(envValFloat)
  431. }
  432. }
  433. }
  434. }
  435. eachName(f.Name, func(name string) {
  436. if f.Destination != nil {
  437. set.Float64Var(f.Destination, name, f.Value, f.Usage)
  438. return
  439. }
  440. set.Float64(name, f.Value, f.Usage)
  441. })
  442. }
  443. // GetName returns the name of the flag.
  444. func (f Float64Flag) GetName() string {
  445. return f.Name
  446. }
  447. func visibleFlags(fl []Flag) []Flag {
  448. visible := []Flag{}
  449. for _, flag := range fl {
  450. if !reflect.ValueOf(flag).FieldByName("Hidden").Bool() {
  451. visible = append(visible, flag)
  452. }
  453. }
  454. return visible
  455. }
  456. func prefixFor(name string) (prefix string) {
  457. if len(name) == 1 {
  458. prefix = "-"
  459. } else {
  460. prefix = "--"
  461. }
  462. return
  463. }
  464. // Returns the placeholder, if any, and the unquoted usage string.
  465. func unquoteUsage(usage string) (string, string) {
  466. for i := 0; i < len(usage); i++ {
  467. if usage[i] == '`' {
  468. for j := i + 1; j < len(usage); j++ {
  469. if usage[j] == '`' {
  470. name := usage[i+1 : j]
  471. usage = usage[:i] + name + usage[j+1:]
  472. return name, usage
  473. }
  474. }
  475. break
  476. }
  477. }
  478. return "", usage
  479. }
  480. func prefixedNames(fullName, placeholder string) string {
  481. var prefixed string
  482. parts := strings.Split(fullName, ",")
  483. for i, name := range parts {
  484. name = strings.Trim(name, " ")
  485. prefixed += prefixFor(name) + name
  486. if placeholder != "" {
  487. prefixed += " " + placeholder
  488. }
  489. if i < len(parts)-1 {
  490. prefixed += ", "
  491. }
  492. }
  493. return prefixed
  494. }
  495. func withEnvHint(envVar, str string) string {
  496. envText := ""
  497. if envVar != "" {
  498. prefix := "$"
  499. suffix := ""
  500. sep := ", $"
  501. if runtime.GOOS == "windows" {
  502. prefix = "%"
  503. suffix = "%"
  504. sep = "%, %"
  505. }
  506. envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix)
  507. }
  508. return str + envText
  509. }
  510. func stringifyFlag(f Flag) string {
  511. fv := reflect.ValueOf(f)
  512. switch f.(type) {
  513. case IntSliceFlag:
  514. return withEnvHint(fv.FieldByName("EnvVar").String(),
  515. stringifyIntSliceFlag(f.(IntSliceFlag)))
  516. case StringSliceFlag:
  517. return withEnvHint(fv.FieldByName("EnvVar").String(),
  518. stringifyStringSliceFlag(f.(StringSliceFlag)))
  519. }
  520. placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
  521. needsPlaceholder := false
  522. defaultValueString := ""
  523. val := fv.FieldByName("Value")
  524. if val.IsValid() {
  525. needsPlaceholder = true
  526. defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface())
  527. if val.Kind() == reflect.String && val.String() != "" {
  528. defaultValueString = fmt.Sprintf(" (default: %q)", val.String())
  529. }
  530. }
  531. if defaultValueString == " (default: )" {
  532. defaultValueString = ""
  533. }
  534. if needsPlaceholder && placeholder == "" {
  535. placeholder = defaultPlaceholder
  536. }
  537. usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
  538. return withEnvHint(fv.FieldByName("EnvVar").String(),
  539. fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault))
  540. }
  541. func stringifyIntSliceFlag(f IntSliceFlag) string {
  542. defaultVals := []string{}
  543. if f.Value != nil && len(f.Value.Value()) > 0 {
  544. for _, i := range f.Value.Value() {
  545. defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
  546. }
  547. }
  548. return stringifySliceFlag(f.Usage, f.Name, defaultVals)
  549. }
  550. func stringifyStringSliceFlag(f StringSliceFlag) string {
  551. defaultVals := []string{}
  552. if f.Value != nil && len(f.Value.Value()) > 0 {
  553. for _, s := range f.Value.Value() {
  554. if len(s) > 0 {
  555. defaultVals = append(defaultVals, fmt.Sprintf("%q", s))
  556. }
  557. }
  558. }
  559. return stringifySliceFlag(f.Usage, f.Name, defaultVals)
  560. }
  561. func stringifySliceFlag(usage, name string, defaultVals []string) string {
  562. placeholder, usage := unquoteUsage(usage)
  563. if placeholder == "" {
  564. placeholder = defaultPlaceholder
  565. }
  566. defaultVal := ""
  567. if len(defaultVals) > 0 {
  568. defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", "))
  569. }
  570. usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
  571. return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault)
  572. }