golangflag.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package pflag
  5. import (
  6. goflag "flag"
  7. "reflect"
  8. "strings"
  9. "time"
  10. )
  11. // go test flags prefixes
  12. func isGotestFlag(flag string) bool {
  13. return strings.HasPrefix(flag, "-test.")
  14. }
  15. func isGotestShorthandFlag(flag string) bool {
  16. return strings.HasPrefix(flag, "test.")
  17. }
  18. // flagValueWrapper implements pflag.Value around a flag.Value. The main
  19. // difference here is the addition of the Type method that returns a string
  20. // name of the type. As this is generally unknown, we approximate that with
  21. // reflection.
  22. type flagValueWrapper struct {
  23. inner goflag.Value
  24. flagType string
  25. }
  26. // We are just copying the boolFlag interface out of goflag as that is what
  27. // they use to decide if a flag should get "true" when no arg is given.
  28. type goBoolFlag interface {
  29. goflag.Value
  30. IsBoolFlag() bool
  31. }
  32. func wrapFlagValue(v goflag.Value) Value {
  33. // If the flag.Value happens to also be a pflag.Value, just use it directly.
  34. if pv, ok := v.(Value); ok {
  35. return pv
  36. }
  37. pv := &flagValueWrapper{
  38. inner: v,
  39. }
  40. t := reflect.TypeOf(v)
  41. if t.Kind() == reflect.Interface || t.Kind() == reflect.Ptr {
  42. t = t.Elem()
  43. }
  44. pv.flagType = strings.TrimSuffix(t.Name(), "Value")
  45. return pv
  46. }
  47. func (v *flagValueWrapper) String() string {
  48. return v.inner.String()
  49. }
  50. func (v *flagValueWrapper) Set(s string) error {
  51. return v.inner.Set(s)
  52. }
  53. func (v *flagValueWrapper) Type() string {
  54. return v.flagType
  55. }
  56. // PFlagFromGoFlag will return a *pflag.Flag given a *flag.Flag
  57. // If the *flag.Flag.Name was a single character (ex: `v`) it will be accessiblei
  58. // with both `-v` and `--v` in flags. If the golang flag was more than a single
  59. // character (ex: `verbose`) it will only be accessible via `--verbose`
  60. func PFlagFromGoFlag(goflag *goflag.Flag) *Flag {
  61. // Remember the default value as a string; it won't change.
  62. flag := &Flag{
  63. Name: goflag.Name,
  64. Usage: goflag.Usage,
  65. Value: wrapFlagValue(goflag.Value),
  66. // Looks like golang flags don't set DefValue correctly :-(
  67. //DefValue: goflag.DefValue,
  68. DefValue: goflag.Value.String(),
  69. }
  70. // Ex: if the golang flag was -v, allow both -v and --v to work
  71. if len(flag.Name) == 1 {
  72. flag.Shorthand = flag.Name
  73. }
  74. if fv, ok := goflag.Value.(goBoolFlag); ok && fv.IsBoolFlag() {
  75. flag.NoOptDefVal = "true"
  76. }
  77. return flag
  78. }
  79. // AddGoFlag will add the given *flag.Flag to the pflag.FlagSet
  80. func (f *FlagSet) AddGoFlag(goflag *goflag.Flag) {
  81. if f.Lookup(goflag.Name) != nil {
  82. return
  83. }
  84. newflag := PFlagFromGoFlag(goflag)
  85. f.AddFlag(newflag)
  86. }
  87. // AddGoFlagSet will add the given *flag.FlagSet to the pflag.FlagSet
  88. func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) {
  89. if newSet == nil {
  90. return
  91. }
  92. newSet.VisitAll(func(goflag *goflag.Flag) {
  93. f.AddGoFlag(goflag)
  94. })
  95. if f.addedGoFlagSets == nil {
  96. f.addedGoFlagSets = make([]*goflag.FlagSet, 0)
  97. }
  98. f.addedGoFlagSets = append(f.addedGoFlagSets, newSet)
  99. }
  100. // CopyToGoFlagSet will add all current flags to the given Go flag set.
  101. // Deprecation remarks get copied into the usage description.
  102. // Whenever possible, a flag gets added for which Go flags shows
  103. // a proper type in the help message.
  104. func (f *FlagSet) CopyToGoFlagSet(newSet *goflag.FlagSet) {
  105. f.VisitAll(func(flag *Flag) {
  106. usage := flag.Usage
  107. if flag.Deprecated != "" {
  108. usage += " (DEPRECATED: " + flag.Deprecated + ")"
  109. }
  110. switch value := flag.Value.(type) {
  111. case *stringValue:
  112. newSet.StringVar((*string)(value), flag.Name, flag.DefValue, usage)
  113. case *intValue:
  114. newSet.IntVar((*int)(value), flag.Name, *(*int)(value), usage)
  115. case *int64Value:
  116. newSet.Int64Var((*int64)(value), flag.Name, *(*int64)(value), usage)
  117. case *uintValue:
  118. newSet.UintVar((*uint)(value), flag.Name, *(*uint)(value), usage)
  119. case *uint64Value:
  120. newSet.Uint64Var((*uint64)(value), flag.Name, *(*uint64)(value), usage)
  121. case *durationValue:
  122. newSet.DurationVar((*time.Duration)(value), flag.Name, *(*time.Duration)(value), usage)
  123. case *float64Value:
  124. newSet.Float64Var((*float64)(value), flag.Name, *(*float64)(value), usage)
  125. default:
  126. newSet.Var(flag.Value, flag.Name, usage)
  127. }
  128. })
  129. }
  130. // ParseSkippedFlags explicitly Parses go test flags (i.e. the one starting with '-test.') with goflag.Parse(),
  131. // since by default those are skipped by pflag.Parse().
  132. // Typical usage example: `ParseGoTestFlags(os.Args[1:], goflag.CommandLine)`
  133. func ParseSkippedFlags(osArgs []string, goFlagSet *goflag.FlagSet) error {
  134. var skippedFlags []string
  135. for _, f := range osArgs {
  136. if isGotestFlag(f) {
  137. skippedFlags = append(skippedFlags, f)
  138. }
  139. }
  140. return goFlagSet.Parse(skippedFlags)
  141. }