add slice support
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
		
							
								
								
									
										236
									
								
								env.go
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								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 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user