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.

636 lines
15 KiB

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