| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- package configflag
- import (
- "flag"
- "fmt"
- "os"
- "reflect"
- "strconv"
- "strings"
- "time"
- "github.com/ncarlier/webhookd/pkg/helper"
- )
- // Bind conf struct tags with flags
- func Bind(conf interface{}, envPrefix string) error {
- return bind(conf, envPrefix, "")
- }
- func bind(conf interface{}, envPrefix, keyPrefix string) error {
- rv := reflect.ValueOf(conf)
- for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
- rv = rv.Elem()
- }
- typ := rv.Type()
- for i := 0; i < typ.NumField(); i++ {
- fieldType := typ.Field(i)
- field := rv.Field(i)
- var key, desc, val string
- // Get field key from struct tag
- if tag, ok := fieldType.Tag.Lookup("flag"); ok {
- key = tag
- } else {
- continue
- }
- // Get field description from struct tag
- if tag, ok := fieldType.Tag.Lookup("desc"); ok {
- desc = tag
- }
- // Get field value from struct tag
- if tag, ok := fieldType.Tag.Lookup("default"); ok {
- val = tag
- }
- if keyPrefix != "" {
- key = keyPrefix + "-" + key
- }
- // Get field value and description from environment variable
- val = getEnvValue(envPrefix, key, val)
- desc = getEnvDesc(envPrefix, key, desc)
- // Get field value by reflection from struct definition
- // And bind value to command line flag
- switch fieldType.Type.Kind() {
- case reflect.String:
- field.SetString(val)
- ptr, _ := field.Addr().Interface().(*string)
- flag.StringVar(ptr, key, val, desc)
- case reflect.Bool:
- bVal, err := strconv.ParseBool(val)
- if err != nil {
- return fmt.Errorf("invalid boolean value for %s: %v", key, err)
- }
- field.SetBool(bVal)
- ptr, _ := field.Addr().Interface().(*bool)
- flag.BoolVar(ptr, key, bVal, desc)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if field.Kind() == reflect.Int64 && field.Type().PkgPath() == "time" && field.Type().Name() == "Duration" {
- d, err := time.ParseDuration(val)
- if err != nil {
- return fmt.Errorf("invalid duration value for %s: %v", key, err)
- }
- field.SetInt(int64(d))
- ptr, _ := field.Addr().Interface().(*time.Duration)
- flag.DurationVar(ptr, key, d, desc)
- } else {
- i64Val, err := strconv.ParseInt(val, 0, fieldType.Type.Bits())
- if err != nil {
- return fmt.Errorf("invalid number value for %s: %v", key, err)
- }
- field.SetInt(i64Val)
- ptr, _ := field.Addr().Interface().(*int)
- flag.IntVar(ptr, key, int(i64Val), desc)
- }
- case reflect.Struct:
- if err := bind(field.Addr().Interface(), envPrefix, key); err != nil {
- return fmt.Errorf("invalid struct value for %s: %v", key, err)
- }
- case reflect.Slice:
- sliceType := field.Type().Elem()
- if sliceType.Kind() == reflect.String {
- vals := strings.Split(val, ",")
- sl := make([]string, len(vals))
- copy(sl, vals)
- field.Set(reflect.ValueOf(sl))
- ptr, _ := field.Addr().Interface().(*[]string)
- af := newArrayFlags(ptr)
- flag.Var(af, key, desc)
- }
- }
- }
- return nil
- }
- func getEnvKey(prefix, key string) string {
- return helper.ToScreamingSnake(prefix + "_" + key)
- }
- func getEnvValue(prefix, key, fallback string) string {
- if value, ok := os.LookupEnv(getEnvKey(prefix, key)); ok {
- return value
- }
- return fallback
- }
- func getEnvDesc(prefix, key, desc string) string {
- return fmt.Sprintf("%s (env: %s)", desc, getEnvKey(prefix, key))
- }
|