223 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package options
 | 
						|
 | 
						|
import (
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/spf13/cast"
 | 
						|
	mreflect "go.unistack.org/micro/v4/util/reflect"
 | 
						|
)
 | 
						|
 | 
						|
// Options interface must be used by all options
 | 
						|
type Validator interface {
 | 
						|
	// Validate returns nil, if all options are correct,
 | 
						|
	// otherwise returns an error explaining the mistake
 | 
						|
	Validate() error
 | 
						|
}
 | 
						|
 | 
						|
// Option func signature
 | 
						|
type Option func(interface{}) error
 | 
						|
 | 
						|
// Apply assign options to struct src
 | 
						|
func Apply(src interface{}, opts ...Option) error {
 | 
						|
	for _, opt := range opts {
 | 
						|
		if err := opt(src); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// SetValueByPath set src struct field to val dst via path
 | 
						|
func SetValueByPath(src interface{}, dst interface{}, path string) error {
 | 
						|
	var err error
 | 
						|
 | 
						|
	switch v := dst.(type) {
 | 
						|
	case []interface{}:
 | 
						|
		if len(v) == 1 {
 | 
						|
			dst = v[0]
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	var sv reflect.Value
 | 
						|
	switch t := src.(type) {
 | 
						|
	case reflect.Value:
 | 
						|
		sv = t
 | 
						|
	default:
 | 
						|
		sv = reflect.ValueOf(src)
 | 
						|
	}
 | 
						|
 | 
						|
	parts := strings.Split(path, ".")
 | 
						|
 | 
						|
	for _, p := range parts {
 | 
						|
 | 
						|
		if sv.Kind() == reflect.Ptr {
 | 
						|
			sv = sv.Elem()
 | 
						|
		}
 | 
						|
		if sv.Kind() != reflect.Struct {
 | 
						|
			return mreflect.ErrInvalidStruct
 | 
						|
		}
 | 
						|
 | 
						|
		typ := sv.Type()
 | 
						|
		for idx := 0; idx < typ.NumField(); idx++ {
 | 
						|
			fld := typ.Field(idx)
 | 
						|
			val := sv.Field(idx)
 | 
						|
 | 
						|
			/*
 | 
						|
				if len(fld.PkgPath) != 0 {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			*/
 | 
						|
 | 
						|
			if fld.Anonymous {
 | 
						|
				if len(parts) == 1 && val.Kind() == reflect.Struct {
 | 
						|
					if err = SetValueByPath(val, dst, p); err != nil {
 | 
						|
						return err
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if fld.Name != p && !strings.EqualFold(strings.ToLower(fld.Name), strings.ToLower(p)) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			switch val.Interface().(type) {
 | 
						|
			case []time.Duration:
 | 
						|
				dst, err = cast.ToDurationSliceE(dst)
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				reflect.Copy(val, reflect.ValueOf(dst))
 | 
						|
				return nil
 | 
						|
			case time.Duration:
 | 
						|
				dst, err = cast.ToDurationE(dst)
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				val.Set(reflect.ValueOf(dst))
 | 
						|
				return nil
 | 
						|
			case time.Time:
 | 
						|
				dst, err = cast.ToTimeE(dst)
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				val.Set(reflect.ValueOf(dst))
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
 | 
						|
			switch val.Kind() {
 | 
						|
			case reflect.Map:
 | 
						|
				if val.IsZero() {
 | 
						|
					val.Set(reflect.MakeMap(val.Type()))
 | 
						|
				}
 | 
						|
 | 
						|
				return setMap(val.Interface(), dst)
 | 
						|
			case reflect.Array, reflect.Slice:
 | 
						|
				switch val.Type().Elem().Kind() {
 | 
						|
				case reflect.Bool:
 | 
						|
					dst, err = cast.ToBoolSliceE(dst)
 | 
						|
				case reflect.String:
 | 
						|
					dst, err = cast.ToStringSliceE(dst)
 | 
						|
				case reflect.Float32:
 | 
						|
					dst, err = toFloat32SliceE(dst)
 | 
						|
				case reflect.Float64:
 | 
						|
					dst, err = toFloat64SliceE(dst)
 | 
						|
				case reflect.Int8:
 | 
						|
					dst, err = toInt8SliceE(dst)
 | 
						|
				case reflect.Int:
 | 
						|
					dst, err = cast.ToIntSliceE(dst)
 | 
						|
				case reflect.Int16:
 | 
						|
					dst, err = toInt16SliceE(dst)
 | 
						|
				case reflect.Int32:
 | 
						|
					dst, err = toInt32SliceE(dst)
 | 
						|
				case reflect.Int64:
 | 
						|
					dst, err = toInt64SliceE(dst)
 | 
						|
				case reflect.Uint8:
 | 
						|
					dst, err = toUint8SliceE(dst)
 | 
						|
				case reflect.Uint:
 | 
						|
					dst, err = toUintSliceE(dst)
 | 
						|
				case reflect.Uint16:
 | 
						|
					dst, err = toUint16SliceE(dst)
 | 
						|
				case reflect.Uint32:
 | 
						|
					dst, err = toUint32SliceE(dst)
 | 
						|
				case reflect.Uint64:
 | 
						|
					dst, err = toUint64SliceE(dst)
 | 
						|
				}
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				if val.Kind() == reflect.Slice {
 | 
						|
					val.Set(reflect.ValueOf(dst))
 | 
						|
				} else {
 | 
						|
					reflect.Copy(val, reflect.ValueOf(dst))
 | 
						|
				}
 | 
						|
				return nil
 | 
						|
			case reflect.Float32:
 | 
						|
				dst, err = toFloat32SliceE(dst)
 | 
						|
			case reflect.Float64:
 | 
						|
				dst, err = toFloat64SliceE(dst)
 | 
						|
			case reflect.Bool:
 | 
						|
				dst, err = cast.ToBoolE(dst)
 | 
						|
			case reflect.String:
 | 
						|
				dst, err = cast.ToStringE(dst)
 | 
						|
			case reflect.Int8:
 | 
						|
				dst, err = cast.ToInt8E(dst)
 | 
						|
			case reflect.Int:
 | 
						|
				dst, err = cast.ToIntE(dst)
 | 
						|
			case reflect.Int16:
 | 
						|
				dst, err = cast.ToInt16E(dst)
 | 
						|
			case reflect.Int32:
 | 
						|
				dst, err = cast.ToInt32E(dst)
 | 
						|
			case reflect.Int64:
 | 
						|
				dst, err = cast.ToInt64E(dst)
 | 
						|
			case reflect.Uint8:
 | 
						|
				dst, err = cast.ToUint8E(dst)
 | 
						|
			case reflect.Uint:
 | 
						|
				dst, err = cast.ToUintE(dst)
 | 
						|
			case reflect.Uint16:
 | 
						|
				dst, err = cast.ToUint16E(dst)
 | 
						|
			case reflect.Uint32:
 | 
						|
				dst, err = cast.ToUint32E(dst)
 | 
						|
			case reflect.Uint64:
 | 
						|
				dst, err = cast.ToUint64E(dst)
 | 
						|
			default:
 | 
						|
			}
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			val.Set(reflect.ValueOf(dst))
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// NewOption create new option with name
 | 
						|
func NewOption(name string) func(...interface{}) Option {
 | 
						|
	return func(dst ...interface{}) Option {
 | 
						|
		return func(src interface{}) error {
 | 
						|
			return SetValueByPath(src, dst, name)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	Address   = NewOption("Address")
 | 
						|
	Name      = NewOption("Name")
 | 
						|
	Broker    = NewOption("Broker")
 | 
						|
	Logger    = NewOption("Logger")
 | 
						|
	Meter     = NewOption("Meter")
 | 
						|
	Tracer    = NewOption("Tracer")
 | 
						|
	Store     = NewOption("Store")
 | 
						|
	Register  = NewOption("Register")
 | 
						|
	Router    = NewOption("Router")
 | 
						|
	Codec     = NewOption("Codec")
 | 
						|
	Codecs    = NewOption("Codecs")
 | 
						|
	Client    = NewOption("Client")
 | 
						|
	Context   = NewOption("Context")
 | 
						|
	TLSConfig = NewOption("TLSConfig")
 | 
						|
	Metadata  = NewOption("Metadata")
 | 
						|
	Timeout   = NewOption("Timeout")
 | 
						|
)
 |