flag.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. package cli
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "strconv"
  7. "strings"
  8. )
  9. // This flag enables bash-completion for all commands and subcommands
  10. var BashCompletionFlag = BoolFlag{
  11. Name: "generate-bash-completion",
  12. }
  13. // This flag prints the version for the application
  14. var VersionFlag = BoolFlag{
  15. Name: "version, v",
  16. Usage: "print the version",
  17. }
  18. // This flag prints the help for all commands and subcommands
  19. var HelpFlag = BoolFlag{
  20. Name: "help, h",
  21. Usage: "show help",
  22. }
  23. // Flag is a common interface related to parsing flags in cli.
  24. // For more advanced flag parsing techniques, it is recomended that
  25. // this interface be implemented.
  26. type Flag interface {
  27. fmt.Stringer
  28. // Apply Flag settings to the given flag set
  29. Apply(*flag.FlagSet)
  30. getName() string
  31. }
  32. func flagSet(name string, flags []Flag) *flag.FlagSet {
  33. set := flag.NewFlagSet(name, flag.ContinueOnError)
  34. for _, f := range flags {
  35. f.Apply(set)
  36. }
  37. return set
  38. }
  39. func eachName(longName string, fn func(string)) {
  40. parts := strings.Split(longName, ",")
  41. for _, name := range parts {
  42. name = strings.Trim(name, " ")
  43. fn(name)
  44. }
  45. }
  46. // Generic is a generic parseable type identified by a specific flag
  47. type Generic interface {
  48. Set(value string) error
  49. String() string
  50. }
  51. // GenericFlag is the flag type for types implementing Generic
  52. type GenericFlag struct {
  53. Name string
  54. Value Generic
  55. Usage string
  56. EnvVar string
  57. }
  58. func (f GenericFlag) String() string {
  59. return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage))
  60. }
  61. func (f GenericFlag) Apply(set *flag.FlagSet) {
  62. val := f.Value
  63. if f.EnvVar != "" {
  64. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  65. val.Set(envVal)
  66. }
  67. }
  68. eachName(f.Name, func(name string) {
  69. set.Var(f.Value, name, f.Usage)
  70. })
  71. }
  72. func (f GenericFlag) getName() string {
  73. return f.Name
  74. }
  75. type StringSlice []string
  76. func (f *StringSlice) Set(value string) error {
  77. *f = append(*f, value)
  78. return nil
  79. }
  80. func (f *StringSlice) String() string {
  81. return fmt.Sprintf("%s", *f)
  82. }
  83. func (f *StringSlice) Value() []string {
  84. return *f
  85. }
  86. type StringSliceFlag struct {
  87. Name string
  88. Value *StringSlice
  89. Usage string
  90. EnvVar string
  91. }
  92. func (f StringSliceFlag) String() string {
  93. firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
  94. pref := prefixFor(firstName)
  95. return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
  96. }
  97. func (f StringSliceFlag) Apply(set *flag.FlagSet) {
  98. if f.EnvVar != "" {
  99. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  100. newVal := &StringSlice{}
  101. for _, s := range strings.Split(envVal, ",") {
  102. newVal.Set(s)
  103. }
  104. f.Value = newVal
  105. }
  106. }
  107. eachName(f.Name, func(name string) {
  108. set.Var(f.Value, name, f.Usage)
  109. })
  110. }
  111. func (f StringSliceFlag) getName() string {
  112. return f.Name
  113. }
  114. type IntSlice []int
  115. func (f *IntSlice) Set(value string) error {
  116. tmp, err := strconv.Atoi(value)
  117. if err != nil {
  118. return err
  119. } else {
  120. *f = append(*f, tmp)
  121. }
  122. return nil
  123. }
  124. func (f *IntSlice) String() string {
  125. return fmt.Sprintf("%d", *f)
  126. }
  127. func (f *IntSlice) Value() []int {
  128. return *f
  129. }
  130. type IntSliceFlag struct {
  131. Name string
  132. Value *IntSlice
  133. Usage string
  134. EnvVar string
  135. }
  136. func (f IntSliceFlag) String() string {
  137. firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
  138. pref := prefixFor(firstName)
  139. return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
  140. }
  141. func (f IntSliceFlag) Apply(set *flag.FlagSet) {
  142. if f.EnvVar != "" {
  143. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  144. newVal := &IntSlice{}
  145. for _, s := range strings.Split(envVal, ",") {
  146. err := newVal.Set(s)
  147. if err != nil {
  148. fmt.Fprintf(os.Stderr, err.Error())
  149. }
  150. }
  151. f.Value = newVal
  152. }
  153. }
  154. eachName(f.Name, func(name string) {
  155. set.Var(f.Value, name, f.Usage)
  156. })
  157. }
  158. func (f IntSliceFlag) getName() string {
  159. return f.Name
  160. }
  161. type BoolFlag struct {
  162. Name string
  163. Usage string
  164. EnvVar string
  165. }
  166. func (f BoolFlag) String() string {
  167. return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
  168. }
  169. func (f BoolFlag) Apply(set *flag.FlagSet) {
  170. val := false
  171. if f.EnvVar != "" {
  172. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  173. envValBool, err := strconv.ParseBool(envVal)
  174. if err == nil {
  175. val = envValBool
  176. }
  177. }
  178. }
  179. eachName(f.Name, func(name string) {
  180. set.Bool(name, val, f.Usage)
  181. })
  182. }
  183. func (f BoolFlag) getName() string {
  184. return f.Name
  185. }
  186. type BoolTFlag struct {
  187. Name string
  188. Usage string
  189. EnvVar string
  190. }
  191. func (f BoolTFlag) String() string {
  192. return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
  193. }
  194. func (f BoolTFlag) Apply(set *flag.FlagSet) {
  195. val := true
  196. if f.EnvVar != "" {
  197. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  198. envValBool, err := strconv.ParseBool(envVal)
  199. if err == nil {
  200. val = envValBool
  201. }
  202. }
  203. }
  204. eachName(f.Name, func(name string) {
  205. set.Bool(name, val, f.Usage)
  206. })
  207. }
  208. func (f BoolTFlag) getName() string {
  209. return f.Name
  210. }
  211. type StringFlag struct {
  212. Name string
  213. Value string
  214. Usage string
  215. EnvVar string
  216. }
  217. func (f StringFlag) String() string {
  218. var fmtString string
  219. fmtString = "%s %v\t%v"
  220. if len(f.Value) > 0 {
  221. fmtString = "%s '%v'\t%v"
  222. } else {
  223. fmtString = "%s %v\t%v"
  224. }
  225. return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage))
  226. }
  227. func (f StringFlag) Apply(set *flag.FlagSet) {
  228. if f.EnvVar != "" {
  229. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  230. f.Value = envVal
  231. }
  232. }
  233. eachName(f.Name, func(name string) {
  234. set.String(name, f.Value, f.Usage)
  235. })
  236. }
  237. func (f StringFlag) getName() string {
  238. return f.Name
  239. }
  240. type IntFlag struct {
  241. Name string
  242. Value int
  243. Usage string
  244. EnvVar string
  245. }
  246. func (f IntFlag) String() string {
  247. return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage))
  248. }
  249. func (f IntFlag) Apply(set *flag.FlagSet) {
  250. if f.EnvVar != "" {
  251. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  252. envValInt, err := strconv.ParseUint(envVal, 10, 64)
  253. if err == nil {
  254. f.Value = int(envValInt)
  255. }
  256. }
  257. }
  258. eachName(f.Name, func(name string) {
  259. set.Int(name, f.Value, f.Usage)
  260. })
  261. }
  262. func (f IntFlag) getName() string {
  263. return f.Name
  264. }
  265. type Float64Flag struct {
  266. Name string
  267. Value float64
  268. Usage string
  269. EnvVar string
  270. }
  271. func (f Float64Flag) String() string {
  272. return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage))
  273. }
  274. func (f Float64Flag) Apply(set *flag.FlagSet) {
  275. if f.EnvVar != "" {
  276. if envVal := os.Getenv(f.EnvVar); envVal != "" {
  277. envValFloat, err := strconv.ParseFloat(envVal, 10)
  278. if err == nil {
  279. f.Value = float64(envValFloat)
  280. }
  281. }
  282. }
  283. eachName(f.Name, func(name string) {
  284. set.Float64(name, f.Value, f.Usage)
  285. })
  286. }
  287. func (f Float64Flag) getName() string {
  288. return f.Name
  289. }
  290. func prefixFor(name string) (prefix string) {
  291. if len(name) == 1 {
  292. prefix = "-"
  293. } else {
  294. prefix = "--"
  295. }
  296. return
  297. }
  298. func prefixedNames(fullName string) (prefixed string) {
  299. parts := strings.Split(fullName, ",")
  300. for i, name := range parts {
  301. name = strings.Trim(name, " ")
  302. prefixed += prefixFor(name) + name
  303. if i < len(parts)-1 {
  304. prefixed += ", "
  305. }
  306. }
  307. return
  308. }
  309. func withEnvHint(envVar, str string) string {
  310. envText := ""
  311. if envVar != "" {
  312. envText = fmt.Sprintf(" [$%s]", envVar)
  313. }
  314. return str + envText
  315. }