Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

262 рядки
7.3 KiB

  1. package altsrc
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "syscall"
  7. "gopkg.in/urfave/cli.v1"
  8. )
  9. // FlagInputSourceExtension is an extension interface of cli.Flag that
  10. // allows a value to be set on the existing parsed flags.
  11. type FlagInputSourceExtension interface {
  12. cli.Flag
  13. ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error
  14. }
  15. // ApplyInputSourceValues iterates over all provided flags and
  16. // executes ApplyInputSourceValue on flags implementing the
  17. // FlagInputSourceExtension interface to initialize these flags
  18. // to an alternate input source.
  19. func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error {
  20. for _, f := range flags {
  21. inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension)
  22. if isType {
  23. err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext)
  24. if err != nil {
  25. return err
  26. }
  27. }
  28. }
  29. return nil
  30. }
  31. // InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
  32. // input source based on the func provided. If there is no error it will then apply the new input source to any flags
  33. // that are supported by the input source
  34. func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc {
  35. return func(context *cli.Context) error {
  36. inputSource, err := createInputSource()
  37. if err != nil {
  38. return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error())
  39. }
  40. return ApplyInputSourceValues(context, inputSource, flags)
  41. }
  42. }
  43. // InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
  44. // input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is
  45. // no error it will then apply the new input source to any flags that are supported by the input source
  46. func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFunc {
  47. return func(context *cli.Context) error {
  48. inputSource, err := createInputSource(context)
  49. if err != nil {
  50. return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error())
  51. }
  52. return ApplyInputSourceValues(context, inputSource, flags)
  53. }
  54. }
  55. // ApplyInputSourceValue applies a generic value to the flagSet if required
  56. func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  57. if f.set != nil {
  58. if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
  59. value, err := isc.Generic(f.GenericFlag.Name)
  60. if err != nil {
  61. return err
  62. }
  63. if value != nil {
  64. eachName(f.Name, func(name string) {
  65. f.set.Set(f.Name, value.String())
  66. })
  67. }
  68. }
  69. }
  70. return nil
  71. }
  72. // ApplyInputSourceValue applies a StringSlice value to the flagSet if required
  73. func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  74. if f.set != nil {
  75. if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
  76. value, err := isc.StringSlice(f.StringSliceFlag.Name)
  77. if err != nil {
  78. return err
  79. }
  80. if value != nil {
  81. var sliceValue cli.StringSlice = value
  82. eachName(f.Name, func(name string) {
  83. underlyingFlag := f.set.Lookup(f.Name)
  84. if underlyingFlag != nil {
  85. underlyingFlag.Value = &sliceValue
  86. }
  87. })
  88. }
  89. }
  90. }
  91. return nil
  92. }
  93. // ApplyInputSourceValue applies a IntSlice value if required
  94. func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  95. if f.set != nil {
  96. if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
  97. value, err := isc.IntSlice(f.IntSliceFlag.Name)
  98. if err != nil {
  99. return err
  100. }
  101. if value != nil {
  102. var sliceValue cli.IntSlice = value
  103. eachName(f.Name, func(name string) {
  104. underlyingFlag := f.set.Lookup(f.Name)
  105. if underlyingFlag != nil {
  106. underlyingFlag.Value = &sliceValue
  107. }
  108. })
  109. }
  110. }
  111. }
  112. return nil
  113. }
  114. // ApplyInputSourceValue applies a Bool value to the flagSet if required
  115. func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  116. if f.set != nil {
  117. if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
  118. value, err := isc.Bool(f.BoolFlag.Name)
  119. if err != nil {
  120. return err
  121. }
  122. if value {
  123. eachName(f.Name, func(name string) {
  124. f.set.Set(f.Name, strconv.FormatBool(value))
  125. })
  126. }
  127. }
  128. }
  129. return nil
  130. }
  131. // ApplyInputSourceValue applies a BoolT value to the flagSet if required
  132. func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  133. if f.set != nil {
  134. if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
  135. value, err := isc.BoolT(f.BoolTFlag.Name)
  136. if err != nil {
  137. return err
  138. }
  139. if !value {
  140. eachName(f.Name, func(name string) {
  141. f.set.Set(f.Name, strconv.FormatBool(value))
  142. })
  143. }
  144. }
  145. }
  146. return nil
  147. }
  148. // ApplyInputSourceValue applies a String value to the flagSet if required
  149. func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  150. if f.set != nil {
  151. if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
  152. value, err := isc.String(f.StringFlag.Name)
  153. if err != nil {
  154. return err
  155. }
  156. if value != "" {
  157. eachName(f.Name, func(name string) {
  158. f.set.Set(f.Name, value)
  159. })
  160. }
  161. }
  162. }
  163. return nil
  164. }
  165. // ApplyInputSourceValue applies a int value to the flagSet if required
  166. func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  167. if f.set != nil {
  168. if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
  169. value, err := isc.Int(f.IntFlag.Name)
  170. if err != nil {
  171. return err
  172. }
  173. if value > 0 {
  174. eachName(f.Name, func(name string) {
  175. f.set.Set(f.Name, strconv.FormatInt(int64(value), 10))
  176. })
  177. }
  178. }
  179. }
  180. return nil
  181. }
  182. // ApplyInputSourceValue applies a Duration value to the flagSet if required
  183. func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  184. if f.set != nil {
  185. if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
  186. value, err := isc.Duration(f.DurationFlag.Name)
  187. if err != nil {
  188. return err
  189. }
  190. if value > 0 {
  191. eachName(f.Name, func(name string) {
  192. f.set.Set(f.Name, value.String())
  193. })
  194. }
  195. }
  196. }
  197. return nil
  198. }
  199. // ApplyInputSourceValue applies a Float64 value to the flagSet if required
  200. func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
  201. if f.set != nil {
  202. if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
  203. value, err := isc.Float64(f.Float64Flag.Name)
  204. if err != nil {
  205. return err
  206. }
  207. if value > 0 {
  208. floatStr := float64ToString(value)
  209. eachName(f.Name, func(name string) {
  210. f.set.Set(f.Name, floatStr)
  211. })
  212. }
  213. }
  214. }
  215. return nil
  216. }
  217. func isEnvVarSet(envVars string) bool {
  218. for _, envVar := range strings.Split(envVars, ",") {
  219. envVar = strings.TrimSpace(envVar)
  220. if _, ok := syscall.Getenv(envVar); ok {
  221. // TODO: Can't use this for bools as
  222. // set means that it was true or false based on
  223. // Bool flag type, should work for other types
  224. return true
  225. }
  226. }
  227. return false
  228. }
  229. func float64ToString(f float64) string {
  230. return fmt.Sprintf("%v", f)
  231. }
  232. func eachName(longName string, fn func(string)) {
  233. parts := strings.Split(longName, ",")
  234. for _, name := range parts {
  235. name = strings.Trim(name, " ")
  236. fn(name)
  237. }
  238. }