bind.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package configflag
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "reflect"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/ncarlier/webhookd/pkg/helper"
  11. )
  12. // Bind conf struct tags with flags
  13. func Bind(conf interface{}, envPrefix string) error {
  14. return bind(conf, envPrefix, "")
  15. }
  16. func bind(conf interface{}, envPrefix, keyPrefix string) error {
  17. rv := reflect.ValueOf(conf)
  18. for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
  19. rv = rv.Elem()
  20. }
  21. typ := rv.Type()
  22. for i := 0; i < typ.NumField(); i++ {
  23. fieldType := typ.Field(i)
  24. field := rv.Field(i)
  25. var key, desc, val string
  26. // Get field key from struct tag
  27. if tag, ok := fieldType.Tag.Lookup("flag"); ok {
  28. key = tag
  29. } else {
  30. continue
  31. }
  32. // Get field description from struct tag
  33. if tag, ok := fieldType.Tag.Lookup("desc"); ok {
  34. desc = tag
  35. }
  36. // Get field value from struct tag
  37. if tag, ok := fieldType.Tag.Lookup("default"); ok {
  38. val = tag
  39. }
  40. if keyPrefix != "" {
  41. key = keyPrefix + "-" + key
  42. }
  43. // Get field value and description from environment variable
  44. val = getEnvValue(envPrefix, key, val)
  45. desc = getEnvDesc(envPrefix, key, desc)
  46. // Get field value by reflection from struct definition
  47. // And bind value to command line flag
  48. switch fieldType.Type.Kind() {
  49. case reflect.String:
  50. field.SetString(val)
  51. ptr, _ := field.Addr().Interface().(*string)
  52. flag.StringVar(ptr, key, val, desc)
  53. case reflect.Bool:
  54. bVal, err := strconv.ParseBool(val)
  55. if err != nil {
  56. return fmt.Errorf("invalid boolean value for %s: %v", key, err)
  57. }
  58. field.SetBool(bVal)
  59. ptr, _ := field.Addr().Interface().(*bool)
  60. flag.BoolVar(ptr, key, bVal, desc)
  61. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  62. if field.Kind() == reflect.Int64 && field.Type().PkgPath() == "time" && field.Type().Name() == "Duration" {
  63. d, err := time.ParseDuration(val)
  64. if err != nil {
  65. return fmt.Errorf("invalid duration value for %s: %v", key, err)
  66. }
  67. field.SetInt(int64(d))
  68. ptr, _ := field.Addr().Interface().(*time.Duration)
  69. flag.DurationVar(ptr, key, d, desc)
  70. } else {
  71. i64Val, err := strconv.ParseInt(val, 0, fieldType.Type.Bits())
  72. if err != nil {
  73. return fmt.Errorf("invalid number value for %s: %v", key, err)
  74. }
  75. field.SetInt(i64Val)
  76. ptr, _ := field.Addr().Interface().(*int)
  77. flag.IntVar(ptr, key, int(i64Val), desc)
  78. }
  79. case reflect.Struct:
  80. if err := bind(field.Addr().Interface(), envPrefix, key); err != nil {
  81. return fmt.Errorf("invalid struct value for %s: %v", key, err)
  82. }
  83. case reflect.Slice:
  84. sliceType := field.Type().Elem()
  85. if sliceType.Kind() == reflect.String {
  86. vals := strings.Split(val, ",")
  87. sl := make([]string, len(vals))
  88. copy(sl, vals)
  89. field.Set(reflect.ValueOf(sl))
  90. ptr, _ := field.Addr().Interface().(*[]string)
  91. af := newArrayFlags(ptr)
  92. flag.Var(af, key, desc)
  93. }
  94. }
  95. }
  96. return nil
  97. }
  98. func getEnvKey(prefix, key string) string {
  99. return helper.ToScreamingSnake(prefix + "_" + key)
  100. }
  101. func getEnvValue(prefix, key, fallback string) string {
  102. if value, ok := os.LookupEnv(getEnvKey(prefix, key)); ok {
  103. return value
  104. }
  105. return fallback
  106. }
  107. func getEnvDesc(prefix, key, desc string) string {
  108. return fmt.Sprintf("%s (env: %s)", desc, getEnvKey(prefix, key))
  109. }