All checks were successful
		
		
	
	test / test (push) Successful in 3m30s
				
			Reviewed-on: #376 Co-authored-by: Evstigneev Denis <danteevstigneev@yandex.ru> Co-committed-by: Evstigneev Denis <danteevstigneev@yandex.ru>
		
			
				
	
	
		
			364 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package config
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"dario.cat/mergo"
 | |
| 	"github.com/google/uuid"
 | |
| 	"go.unistack.org/micro/v3/options"
 | |
| 	mid "go.unistack.org/micro/v3/util/id"
 | |
| 	rutil "go.unistack.org/micro/v3/util/reflect"
 | |
| 	mtime "go.unistack.org/micro/v3/util/time"
 | |
| )
 | |
| 
 | |
| type defaultConfig struct {
 | |
| 	funcLoad FuncLoad
 | |
| 	funcSave FuncSave
 | |
| 	opts     Options
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) Options() Options {
 | |
| 	return c.opts
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) Init(opts ...Option) error {
 | |
| 	for _, o := range opts {
 | |
| 		o(&c.opts)
 | |
| 	}
 | |
| 
 | |
| 	if err := DefaultBeforeInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	c.funcLoad = c.fnLoad
 | |
| 	c.funcSave = c.fnSave
 | |
| 
 | |
| 	c.opts.Hooks.EachPrev(func(hook options.Hook) {
 | |
| 		switch h := hook.(type) {
 | |
| 		case HookLoad:
 | |
| 			c.funcLoad = h(c.funcLoad)
 | |
| 		case HookSave:
 | |
| 			c.funcSave = h(c.funcSave)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	if err := DefaultAfterInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
 | |
| 	return c.funcLoad(ctx, opts...)
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) fnLoad(ctx context.Context, opts ...LoadOption) error {
 | |
| 	var err error
 | |
| 
 | |
| 	if c.opts.SkipLoad != nil && c.opts.SkipLoad(ctx, c) {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if err = DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	options := NewLoadOptions(opts...)
 | |
| 	mopts := []func(*mergo.Config){mergo.WithTypeCheck}
 | |
| 	if options.Override {
 | |
| 		mopts = append(mopts, mergo.WithOverride)
 | |
| 	}
 | |
| 	if options.Append {
 | |
| 		mopts = append(mopts, mergo.WithAppendSlice)
 | |
| 	}
 | |
| 
 | |
| 	dst := c.opts.Struct
 | |
| 	if options.Struct != nil {
 | |
| 		dst = options.Struct
 | |
| 	}
 | |
| 
 | |
| 	src, err := rutil.Zero(dst)
 | |
| 	if err != nil {
 | |
| 		if !c.opts.AllowFail {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err = DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
 | |
| 		err = mergo.Merge(dst, src, mopts...)
 | |
| 	}
 | |
| 
 | |
| 	if err != nil && !c.opts.AllowFail {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err := DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| //nolint:gocyclo
 | |
| func fillValue(value reflect.Value, val string) error {
 | |
| 	if !rutil.IsEmpty(value) {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	switch value.Kind() {
 | |
| 	case reflect.Map:
 | |
| 		t := value.Type()
 | |
| 		nvals := strings.FieldsFunc(val, func(c rune) bool { return c == ',' || c == ';' })
 | |
| 		if value.IsNil() {
 | |
| 			value.Set(reflect.MakeMapWithSize(t, len(nvals)))
 | |
| 		}
 | |
| 		kt := t.Key()
 | |
| 		et := t.Elem()
 | |
| 		for _, nval := range nvals {
 | |
| 			kv := strings.FieldsFunc(nval, func(c rune) bool { return c == '=' })
 | |
| 			mkey := reflect.Indirect(reflect.New(kt))
 | |
| 			mval := reflect.Indirect(reflect.New(et))
 | |
| 			if err := fillValue(mkey, kv[0]); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			if err := fillValue(mval, kv[1]); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			value.SetMapIndex(mkey, mval)
 | |
| 		}
 | |
| 	case reflect.Slice, reflect.Array:
 | |
| 		nvals := strings.FieldsFunc(val, func(c rune) bool { return c == ',' || c == ';' })
 | |
| 		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 := fillValue(nvalue, nval); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			value.Index(idx).Set(nvalue)
 | |
| 		}
 | |
| 	case reflect.Bool:
 | |
| 		v, err := strconv.ParseBool(val)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		value.Set(reflect.ValueOf(v))
 | |
| 	case reflect.String:
 | |
| 		switch val {
 | |
| 		case "micro:generate uuid":
 | |
| 			uid, err := uuid.NewRandom()
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			val = uid.String()
 | |
| 		case "micro:generate id":
 | |
| 			uid, err := mid.New()
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			val = uid
 | |
| 		}
 | |
| 		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(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:
 | |
| 		switch {
 | |
| 		case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "time":
 | |
| 			v, err := time.ParseDuration(val)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			value.Set(reflect.ValueOf(v))
 | |
| 		case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "go.unistack.org/micro/v3/util/time":
 | |
| 			v, err := mtime.ParseDuration(val)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			value.SetInt(int64(v))
 | |
| 		default:
 | |
| 			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(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(v))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func fillValues(valueOf reflect.Value, tname string) error {
 | |
| 	var values reflect.Value
 | |
| 
 | |
| 	if valueOf.Kind() == reflect.Ptr {
 | |
| 		values = valueOf.Elem()
 | |
| 	} else {
 | |
| 		values = valueOf
 | |
| 	}
 | |
| 
 | |
| 	if values.Kind() == reflect.Invalid {
 | |
| 		return ErrInvalidStruct
 | |
| 	}
 | |
| 
 | |
| 	fields := values.Type()
 | |
| 
 | |
| 	for idx := 0; idx < fields.NumField(); idx++ {
 | |
| 		field := fields.Field(idx)
 | |
| 		value := values.Field(idx)
 | |
| 		if !value.CanSet() {
 | |
| 			continue
 | |
| 		}
 | |
| 		if len(field.PkgPath) != 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		switch value.Kind() {
 | |
| 		case reflect.Struct:
 | |
| 			value.Set(reflect.Indirect(reflect.New(value.Type())))
 | |
| 			if err := fillValues(value, tname); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			continue
 | |
| 		case 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 := fillValues(value, tname); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			continue
 | |
| 		}
 | |
| 		tag, ok := field.Tag.Lookup(tname)
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if err := fillValue(value, tag); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error {
 | |
| 	return c.funcSave(ctx, opts...)
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) fnSave(ctx context.Context, opts ...SaveOption) error {
 | |
| 	if c.opts.SkipSave != nil && c.opts.SkipSave(ctx, c) {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if err := DefaultBeforeSave(ctx, c); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err := DefaultAfterSave(ctx, c); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) String() string {
 | |
| 	return "default"
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) Name() string {
 | |
| 	return c.opts.Name
 | |
| }
 | |
| 
 | |
| func (c *defaultConfig) Watch(_ context.Context, _ ...WatchOption) (Watcher, error) {
 | |
| 	return nil, ErrWatcherNotImplemented
 | |
| }
 | |
| 
 | |
| // NewConfig returns new default config source
 | |
| func NewConfig(opts ...Option) Config {
 | |
| 	options := NewOptions(opts...)
 | |
| 	if len(options.StructTag) == 0 {
 | |
| 		options.StructTag = "default"
 | |
| 	}
 | |
| 	c := &defaultConfig{opts: options}
 | |
| 	c.funcLoad = c.fnLoad
 | |
| 	c.funcSave = c.fnSave
 | |
| 
 | |
| 	return c
 | |
| }
 |