diff --git a/env.go b/env.go index 4eaac52..5de75c8 100644 --- a/env.go +++ b/env.go @@ -4,12 +4,15 @@ import ( "github.com/unistack-org/micro/v3/config" "context" "reflect" + "strings" "os" "strconv" + "errors" ) var ( DefaultStructTag = "env" + ErrInvalidStruct = errors.New("invalid struct specified") ) type envConfig struct { @@ -29,8 +32,131 @@ func (c *envConfig) Init(opts...config.Option) error { } func (c *envConfig) Load(ctx context.Context) error { - fields := reflect.TypeOf(c.opts.Struct).Elem() - values := reflect.ValueOf(c.opts.Struct).Elem() + valueOf := reflect.ValueOf(c.opts.Struct) + + if err := c.fillValues(ctx, valueOf); err != nil { + return err + } + + return nil +} + +func (c *envConfig) fillValue(ctx context.Context, value reflect.Value, val string) error { + switch value.Kind() { + case reflect.Slice, reflect.Array: + nvals := strings.FieldsFunc(val, func(c rune) bool { return c == ',' || c == ';' }) + // value = value.Elem() + value.Set(reflect.MakeSlice(reflect.SliceOf(value.Type().Elem()), len(nvals), len(nvals))) + for idx, nval := range nvals { + nvalue := reflect.Indirect(reflect.New(value.Type().Elem())) + if err := c.fillValue(ctx, nvalue, nval); err != nil { + return err + } + value.Index(idx).Set(nvalue) + //value.Set(reflect.Append(value, nvalue)) + } + case reflect.Bool: + v, err := strconv.ParseBool(val) + if err != nil { + return err + } + value.Set(reflect.ValueOf(v)) + case reflect.String: + value.Set(reflect.ValueOf(val)) + case reflect.Float32: + v, err := strconv.ParseFloat(val, 32) + if err != nil { + return err + } + value.Set(reflect.ValueOf(float32(v))) + case reflect.Float64: + v, err := strconv.ParseFloat(val, 64) + if err != nil { + return err + } + value.Set(reflect.ValueOf(float64(v))) + case reflect.Int: + v, err := strconv.ParseInt(val, 10, 0) + if err != nil { + return err + } + value.Set(reflect.ValueOf(int(v))) + case reflect.Int8: + v, err := strconv.ParseInt(val, 10, 8) + if err != nil { + return err + } + value.Set(reflect.ValueOf(v)) + case reflect.Int16: + v, err := strconv.ParseInt(val, 10, 16) + if err != nil { + return err + } + value.Set(reflect.ValueOf(int16(v))) + case reflect.Int32: + v, err := strconv.ParseInt(val, 10, 32) + if err != nil { + return err + } + value.Set(reflect.ValueOf(int32(v))) + case reflect.Int64: + v, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return err + } + value.Set(reflect.ValueOf(int64(v))) + case reflect.Uint: + v, err := strconv.ParseUint(val, 10, 0) + if err != nil { + return err + } + value.Set(reflect.ValueOf(uint(v))) + case reflect.Uint8: + v, err := strconv.ParseUint(val, 10, 8) + if err != nil { + return err + } + value.Set(reflect.ValueOf(uint8(v))) + case reflect.Uint16: + v, err := strconv.ParseUint(val, 10, 16) + if err != nil { + return err + } + value.Set(reflect.ValueOf(uint16(v))) + case reflect.Uint32: + v, err := strconv.ParseUint(val, 10, 32) + if err != nil { + return err + } + value.Set(reflect.ValueOf(uint32(v))) + case reflect.Uint64: + v, err := strconv.ParseUint(val, 10, 64) + if err != nil { + return err + } + value.Set(reflect.ValueOf(uint64(v))) + } + return nil +} + +func (c *envConfig) fillValues(ctx context.Context, valueOf reflect.Value) error { + var values reflect.Value + + if valueOf.Kind() == reflect.Ptr { + values = valueOf.Elem() + } else { + values = valueOf + } + + if values.Kind() == reflect.Invalid { + return ErrInvalidStruct + } + + // if values.Kind() != reflect.Struct { + // return c.fillValue(ctx, values) + // } + + fields := values.Type() for idx := 0; idx < fields.NumField(); idx++ { field := fields.Field(idx) @@ -38,95 +164,35 @@ func (c *envConfig) Load(ctx context.Context) error { if !value.CanSet() { continue } + if len(field.PkgPath) != 0 { + continue + } + if value.Kind() == reflect.Ptr { + if value.IsNil() { + if value.Type().Elem().Kind() != reflect.Struct { + // nil pointer to a non-struct: leave it alone + break + } + // nil pointer to struct: create a zero instance + value.Set(reflect.New(value.Type().Elem())) + } + value = value.Elem() + if err := c.fillValues(ctx, value); err != nil { + return err + } + continue + } tag, ok := field.Tag.Lookup(c.opts.StructTag) if !ok { - continue + return nil } val, ok := os.LookupEnv(tag) if !ok { - continue + return nil } - switch value.Kind() { - case reflect.Bool: - v, err := strconv.ParseBool(val) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.String: - value.Set(reflect.ValueOf(val)) - case reflect.Float32: - v, err := strconv.ParseFloat(val, 32) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Float64: - v, err := strconv.ParseFloat(val, 64) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Int: - v, err := strconv.ParseInt(val, 10, 0) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Int8: - v, err := strconv.ParseInt(val, 10, 8) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Int16: - v, err := strconv.ParseInt(val, 10, 16) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Int32: - v, err := strconv.ParseInt(val, 10, 32) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Int64: - v, err := strconv.ParseInt(val, 10, 64) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Uint: - v, err := strconv.ParseUint(val, 10, 0) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Uint8: - v, err := strconv.ParseUint(val, 10, 8) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Uint16: - v, err := strconv.ParseUint(val, 10, 16) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Uint32: - v, err := strconv.ParseUint(val, 10, 32) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) - case reflect.Uint64: - v, err := strconv.ParseUint(val, 10, 64) - if err != nil { - return err - } - value.Set(reflect.ValueOf(v)) + + if err := c.fillValue(ctx, value, val); err != nil { + return err } } diff --git a/env_test.go b/env_test.go index 5f8bc2e..39332b4 100644 --- a/env_test.go +++ b/env_test.go @@ -6,12 +6,14 @@ import ( "testing" "context" "os" + "fmt" ) type Config struct { StringValue string `env:"STRING_VALUE"` BoolValue bool `env:"BOOL_VALUE"` StringSlice []string `env:"STRING_SLICE"` + IntSlice []int `env:"INT_SLICE"` } func TestEnv(t *testing.T) { @@ -34,7 +36,8 @@ func TestEnv(t *testing.T) { os.Setenv("STRING_VALUE","STRING_VALUE") os.Setenv("BOOL_VALUE","true") - os.Setenv("STRING_SLICE", "STRING_SLICE1,STRING_SLICE2") + os.Setenv("STRING_SLICE", "STRING_SLICE1,STRING_SLICE2;STRING_SLICE3") + os.Setenv("INT_SLICE", "1,2,3,4,5") if err := cfg.Load(ctx); err !=nil { t.Fatal(err) @@ -47,7 +50,9 @@ func TestEnv(t *testing.T) { t.Fatalf("something wrong with env config: %v", conf) } - if len(conf.StringSlice) != 2 { + if len(conf.StringSlice) != 3 { t.Fatalf("something wrong with env config: %v", conf) } + + fmt.Printf("%#+v\n", conf) }