Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
1de9911b73 | |||
b4092c6619 | |||
024868bfd7 | |||
a0bbfd6d02 | |||
2cb6843773 | |||
87e1480077 | |||
bcd7f6a551 | |||
925b3af46b | |||
ef4efa6a6b | |||
125646d89b | |||
7af7649448 |
@@ -98,6 +98,7 @@ func Encode(e *Endpoint) map[string]string {
|
||||
set("method", strings.Join(e.Method, ","))
|
||||
set("path", strings.Join(e.Path, ","))
|
||||
set("host", strings.Join(e.Host, ","))
|
||||
set("body", e.Body)
|
||||
|
||||
return ep
|
||||
}
|
||||
@@ -118,6 +119,7 @@ func Decode(e metadata.Metadata) *Endpoint {
|
||||
ephost, _ := e.Get("host")
|
||||
ep.Host = []string{ephost}
|
||||
ep.Handler, _ = e.Get("handler")
|
||||
ep.Body, _ = e.Get("body")
|
||||
|
||||
return ep
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ func NewOptions(opts ...Option) Options {
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Name string
|
||||
// Issuer of the service's account
|
||||
Issuer string
|
||||
// ID is the services auth ID
|
||||
@@ -63,6 +64,13 @@ func Addrs(addrs ...string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Name sets the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
||||
// Issuer of the services account
|
||||
func Issuer(i string) Option {
|
||||
return func(o *Options) {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
@@ -40,7 +41,7 @@ func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
|
||||
case *Frame:
|
||||
v.Data = buf
|
||||
default:
|
||||
return ErrInvalidMessage
|
||||
return json.Unmarshal(buf, v)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -64,7 +65,11 @@ func (c *noopCodec) Write(conn io.Writer, m *Message, b interface{}) error {
|
||||
case []byte:
|
||||
v = vb
|
||||
default:
|
||||
return ErrInvalidMessage
|
||||
var err error
|
||||
v, err = json.Marshal(vb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := conn.Write(v)
|
||||
return err
|
||||
@@ -98,30 +103,34 @@ func (c *noopCodec) Marshal(v interface{}) ([]byte, error) {
|
||||
case *Message:
|
||||
return ve.Body, nil
|
||||
}
|
||||
return nil, ErrInvalidMessage
|
||||
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (c *noopCodec) Unmarshal(d []byte, v interface{}) error {
|
||||
var err error
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
switch ve := v.(type) {
|
||||
case string:
|
||||
ve = string(d)
|
||||
return nil
|
||||
case *string:
|
||||
*ve = string(d)
|
||||
return nil
|
||||
case []byte:
|
||||
ve = d
|
||||
return nil
|
||||
case *[]byte:
|
||||
*ve = d
|
||||
return nil
|
||||
case *Frame:
|
||||
ve.Data = d
|
||||
return nil
|
||||
case *Message:
|
||||
ve.Body = d
|
||||
default:
|
||||
err = ErrInvalidMessage
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
return json.Unmarshal(d, v)
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ var (
|
||||
|
||||
// Config is an interface abstraction for dynamic configuration
|
||||
type Config interface {
|
||||
Name() string
|
||||
// Init the config
|
||||
Init(opts ...Option) error
|
||||
// Options in the config
|
||||
|
@@ -251,6 +251,10 @@ func (c *defaultConfig) String() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func (c *defaultConfig) Name() string {
|
||||
return c.opts.Name
|
||||
}
|
||||
|
||||
func NewConfig(opts ...Option) Config {
|
||||
options := NewOptions(opts...)
|
||||
if len(options.StructTag) == 0 {
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Name string
|
||||
AllowFail bool
|
||||
BeforeLoad []func(context.Context, Config) error
|
||||
AfterLoad []func(context.Context, Config) error
|
||||
@@ -116,3 +117,10 @@ func StructTag(name string) Option {
|
||||
o.StructTag = name
|
||||
}
|
||||
}
|
||||
|
||||
// Name sets the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ type Option func(*Options)
|
||||
|
||||
// Options holds logger options
|
||||
type Options struct {
|
||||
Name string
|
||||
// The logging level the logger should log at. default is `InfoLevel`
|
||||
Level Level
|
||||
// fields to always be logged
|
||||
@@ -72,3 +73,10 @@ func WithContext(ctx context.Context) Option {
|
||||
o.Context = ctx
|
||||
}
|
||||
}
|
||||
|
||||
// WithName sets the name
|
||||
func withName(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
@@ -93,3 +93,10 @@ func Label(key, val string) Option {
|
||||
o.Labels.vals = append(o.Labels.vals, val)
|
||||
}
|
||||
}
|
||||
|
||||
// Name sets the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Name string
|
||||
// Addrs is the list of intermediary addresses to connect to
|
||||
Addrs []string
|
||||
// Codec is the codec interface to use where headers are not supported
|
||||
@@ -176,3 +177,10 @@ func Tracer(t tracer.Tracer) Option {
|
||||
o.Tracer = t
|
||||
}
|
||||
}
|
||||
|
||||
// Name sets the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ type Option func(*Options)
|
||||
|
||||
// Options provides network configuration options
|
||||
type Options struct {
|
||||
Name string
|
||||
// Id is tunnel id
|
||||
Id string
|
||||
// Address is tunnel address
|
||||
@@ -181,3 +182,10 @@ func Tracer(t tracer.Tracer) Option {
|
||||
o.Tracer = t
|
||||
}
|
||||
}
|
||||
|
||||
// Name sets the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
40
options.go
40
options.go
@@ -134,6 +134,14 @@ func BrokerServer(n string) BrokerOption {
|
||||
}
|
||||
}
|
||||
|
||||
// Client to be used for service
|
||||
func Client(c ...client.Client) Option {
|
||||
return func(o *Options) error {
|
||||
o.Clients = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Clients to be used for service
|
||||
func Clients(c ...client.Client) Option {
|
||||
return func(o *Options) error {
|
||||
@@ -161,6 +169,14 @@ func Profile(p profile.Profile) Option {
|
||||
}
|
||||
*/
|
||||
|
||||
// Server to be used for service
|
||||
func Server(s ...server.Server) Option {
|
||||
return func(o *Options) error {
|
||||
o.Servers = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Servers to be used for service
|
||||
func Servers(s ...server.Server) Option {
|
||||
return func(o *Options) error {
|
||||
@@ -169,6 +185,14 @@ func Servers(s ...server.Server) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Store sets the store to use
|
||||
func Store(s ...store.Store) Option {
|
||||
return func(o *Options) error {
|
||||
o.Stores = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Stores sets the store to use
|
||||
func Stores(s ...store.Store) Option {
|
||||
return func(o *Options) error {
|
||||
@@ -275,6 +299,14 @@ func LoggerServer(n string) LoggerOption {
|
||||
}
|
||||
*/
|
||||
|
||||
// Meter set the meter to use
|
||||
func Meter(m ...meter.Meter) Option {
|
||||
return func(o *Options) error {
|
||||
o.Meters = m
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Meters set the meter to use
|
||||
func Meters(m ...meter.Meter) Option {
|
||||
return func(o *Options) error {
|
||||
@@ -450,6 +482,14 @@ func Auth(a auth.Auth) Option {
|
||||
}
|
||||
*/
|
||||
|
||||
// Config sets the config for the service
|
||||
func Config(c ...config.Config) Option {
|
||||
return func(o *Options) error {
|
||||
o.Configs = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Configs sets the configs for the service
|
||||
func Configs(c ...config.Config) Option {
|
||||
return func(o *Options) error {
|
||||
|
@@ -246,13 +246,13 @@ func DeregisterDomain(d string) DeregisterOption {
|
||||
}
|
||||
}
|
||||
|
||||
func GetContext(ctx context.Context) LookupOption {
|
||||
func LookupContext(ctx context.Context) LookupOption {
|
||||
return func(o *LookupOptions) {
|
||||
o.Context = ctx
|
||||
}
|
||||
}
|
||||
|
||||
func GetDomain(d string) LookupOption {
|
||||
func LookupDomain(d string) LookupOption {
|
||||
return func(o *LookupOptions) {
|
||||
o.Domain = d
|
||||
}
|
||||
@@ -269,3 +269,10 @@ func ListDomain(d string) ListOption {
|
||||
o.Domain = d
|
||||
}
|
||||
}
|
||||
|
||||
// Name sets the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
@@ -91,6 +91,13 @@ func Meter(m meter.Meter) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Name the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
||||
// Tracer sets the tracer
|
||||
func Tracer(t tracer.Tracer) Option {
|
||||
return func(o *Options) {
|
||||
|
@@ -53,3 +53,10 @@ func NewOptions(opts ...Option) Options {
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// Name sets the name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
@@ -2,24 +2,53 @@ package reflect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
bracketSplitter = regexp.MustCompile(`\[|\]`)
|
||||
ErrInvalidStruct = errors.New("invalid struct specified")
|
||||
ErrInvalidParam = errors.New("invalid url query param provided")
|
||||
)
|
||||
|
||||
func fieldName(name string) string {
|
||||
newstr := make([]rune, 0)
|
||||
upper := false
|
||||
for idx, chr := range name {
|
||||
if idx == 0 {
|
||||
upper = true
|
||||
} else if chr == '_' {
|
||||
upper = true
|
||||
continue
|
||||
}
|
||||
if upper {
|
||||
newstr = append(newstr, unicode.ToUpper(chr))
|
||||
} else {
|
||||
newstr = append(newstr, chr)
|
||||
}
|
||||
upper = false
|
||||
}
|
||||
|
||||
return string(newstr)
|
||||
}
|
||||
|
||||
func IsEmpty(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
switch getKind(v) {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
case reflect.Int:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
case reflect.Uint:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
case reflect.Float32:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
@@ -119,3 +148,450 @@ func CopyFrom(a, b interface{}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func URLMap(query string) (map[string]interface{}, error) {
|
||||
var (
|
||||
mp interface{} = make(map[string]interface{})
|
||||
)
|
||||
|
||||
params := strings.Split(query, "&")
|
||||
|
||||
for _, part := range params {
|
||||
tm, err := queryToMap(part)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp = merge(mp, tm)
|
||||
}
|
||||
|
||||
return mp.(map[string]interface{}), nil
|
||||
}
|
||||
|
||||
func FlattenMap(a map[string]interface{}) map[string]interface{} {
|
||||
// preprocess map
|
||||
nb := make(map[string]interface{}, len(a))
|
||||
for k, v := range a {
|
||||
ps := strings.Split(k, ".")
|
||||
if len(ps) == 1 {
|
||||
nb[k] = v
|
||||
continue
|
||||
}
|
||||
em := make(map[string]interface{})
|
||||
em[ps[len(ps)-1]] = v
|
||||
for i := len(ps) - 2; i > 0; i-- {
|
||||
nm := make(map[string]interface{})
|
||||
nm[ps[i]] = em
|
||||
em = nm
|
||||
}
|
||||
if vm, ok := nb[ps[0]]; ok {
|
||||
// nested map
|
||||
nm := vm.(map[string]interface{})
|
||||
for vk, vv := range em {
|
||||
nm[vk] = vv
|
||||
}
|
||||
nb[ps[0]] = nm
|
||||
} else {
|
||||
nb[ps[0]] = em
|
||||
}
|
||||
}
|
||||
return nb
|
||||
}
|
||||
|
||||
func MergeMap(a interface{}, b map[string]interface{}) error {
|
||||
var err error
|
||||
|
||||
ta := reflect.TypeOf(a)
|
||||
if ta.Kind() == reflect.Ptr {
|
||||
ta = ta.Elem()
|
||||
}
|
||||
va := reflect.ValueOf(a)
|
||||
if va.Kind() == reflect.Ptr {
|
||||
va = va.Elem()
|
||||
}
|
||||
|
||||
for mk, mv := range b {
|
||||
vmv := reflect.ValueOf(mv)
|
||||
name := fieldName(mk)
|
||||
fva := va.FieldByName(name)
|
||||
fta, found := ta.FieldByName(name)
|
||||
if !found || !fva.IsValid() || !fva.CanSet() || fta.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
// fast path via direct assign
|
||||
if vmv.Type().AssignableTo(fta.Type) && !IsEmpty(vmv) {
|
||||
fva.Set(vmv)
|
||||
continue
|
||||
}
|
||||
switch getKind(fva) {
|
||||
case reflect.Bool:
|
||||
err = mergeBool(fva, vmv)
|
||||
case reflect.String:
|
||||
err = mergeString(fva, vmv)
|
||||
case reflect.Int:
|
||||
err = mergeInt(fva, vmv)
|
||||
case reflect.Uint:
|
||||
err = mergeUint(fva, vmv)
|
||||
case reflect.Float32:
|
||||
err = mergeFloat(fva, vmv)
|
||||
case reflect.Array:
|
||||
//fmt.Printf("Array %#+v %#+v\n", fva, vmv)
|
||||
case reflect.Slice:
|
||||
err = mergeSlice(fva, vmv)
|
||||
case reflect.Ptr:
|
||||
if fva.IsNil() {
|
||||
fva.Set(reflect.New(fva.Type().Elem()))
|
||||
if fva.Elem().Type().Kind() == reflect.Struct {
|
||||
for i := 0; i < fva.Elem().NumField(); i++ {
|
||||
field := fva.Elem().Field(i)
|
||||
if field.Type().Kind() == reflect.Ptr && field.IsNil() && fva.Elem().Type().Field(i).Anonymous == true {
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if nmp, ok := vmv.Interface().(map[string]interface{}); ok {
|
||||
err = MergeMap(fva.Interface(), nmp)
|
||||
} else {
|
||||
err = fmt.Errorf("cant fill")
|
||||
}
|
||||
case reflect.Struct:
|
||||
if nmp, ok := vmv.Interface().(map[string]interface{}); ok {
|
||||
err = MergeMap(fva.Interface(), nmp)
|
||||
} else {
|
||||
err = fmt.Errorf("cant fill")
|
||||
}
|
||||
case reflect.Map:
|
||||
//fmt.Printf("Map %#+v %#+v\n", fva, vmv)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeSlice(va, vb reflect.Value) error {
|
||||
switch getKind(vb) {
|
||||
/*
|
||||
case reflect.Int:
|
||||
if vb.Int() == 1 {
|
||||
va.SetBool(true)
|
||||
}
|
||||
case reflect.Uint:
|
||||
if vb.Uint() == 1 {
|
||||
va.SetBool(true)
|
||||
}
|
||||
case reflect.Float64:
|
||||
if vb.Float() == 1 {
|
||||
va.SetBool(true)
|
||||
}
|
||||
*/
|
||||
case reflect.String:
|
||||
var err error
|
||||
fn := func(c rune) bool { return c == ',' || c == ';' || c == ' ' }
|
||||
slice := strings.FieldsFunc(vb.String(), fn)
|
||||
va.Set(reflect.MakeSlice(va.Type(), len(slice), len(slice)))
|
||||
for idx, sl := range slice {
|
||||
vl := reflect.ValueOf(sl)
|
||||
switch va.Type().Elem().Kind() {
|
||||
case reflect.Bool:
|
||||
err = mergeBool(va.Index(idx), vl)
|
||||
case reflect.String:
|
||||
err = mergeString(va.Index(idx), vl)
|
||||
case reflect.Ptr:
|
||||
if va.Index(idx).IsNil() {
|
||||
va.Index(idx).Set(reflect.New(va.Index(idx).Type().Elem()))
|
||||
}
|
||||
switch va.Type().Elem().String() {
|
||||
case "*wrapperspb.BoolValue":
|
||||
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
|
||||
err = mergeBool(eva, vl)
|
||||
}
|
||||
case "*wrapperspb.BytesValue":
|
||||
if eva := va.Index(idx).FieldByName("Value"); eva.IsValid() {
|
||||
err = mergeUint(eva, vl)
|
||||
}
|
||||
case "*wrapperspb.DoubleValue", "*wrapperspb.FloatValue":
|
||||
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
|
||||
err = mergeFloat(eva, vl)
|
||||
}
|
||||
case "*wrapperspb.Int32Value", "*wrapperspb.Int64Value":
|
||||
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
|
||||
err = mergeInt(eva, vl)
|
||||
}
|
||||
case "*wrapperspb.StringValue":
|
||||
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
|
||||
err = mergeString(eva, vl)
|
||||
}
|
||||
case "*wrapperspb.UInt32Value", "*wrapperspb.UInt64Value":
|
||||
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
|
||||
err = mergeUint(eva, vl)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeBool(va, vb reflect.Value) error {
|
||||
switch getKind(vb) {
|
||||
case reflect.Int:
|
||||
if vb.Int() == 1 {
|
||||
va.SetBool(true)
|
||||
}
|
||||
case reflect.Uint:
|
||||
if vb.Uint() == 1 {
|
||||
va.SetBool(true)
|
||||
}
|
||||
case reflect.Float32:
|
||||
if vb.Float() == 1 {
|
||||
va.SetBool(true)
|
||||
}
|
||||
case reflect.String:
|
||||
if b, err := strconv.ParseBool(vb.String()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
va.SetBool(b)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeString(va, vb reflect.Value) error {
|
||||
switch getKind(vb) {
|
||||
case reflect.Int:
|
||||
va.SetString(fmt.Sprintf("%d", vb.Int()))
|
||||
case reflect.Uint:
|
||||
va.SetString(fmt.Sprintf("%d", vb.Uint()))
|
||||
case reflect.Float32:
|
||||
va.SetString(fmt.Sprintf("%f", vb.Float()))
|
||||
case reflect.String:
|
||||
va.Set(vb)
|
||||
default:
|
||||
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeInt(va, vb reflect.Value) error {
|
||||
switch getKind(vb) {
|
||||
case reflect.Int:
|
||||
va.Set(vb)
|
||||
case reflect.Uint:
|
||||
va.SetInt(int64(vb.Uint()))
|
||||
case reflect.Float32:
|
||||
va.SetInt(int64(vb.Float()))
|
||||
case reflect.String:
|
||||
if f, err := strconv.ParseInt(vb.String(), 10, va.Type().Bits()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
va.SetInt(f)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeUint(va, vb reflect.Value) error {
|
||||
switch getKind(vb) {
|
||||
case reflect.Int:
|
||||
va.SetUint(uint64(vb.Int()))
|
||||
case reflect.Uint:
|
||||
va.Set(vb)
|
||||
case reflect.Float32:
|
||||
va.SetUint(uint64(vb.Float()))
|
||||
case reflect.String:
|
||||
if f, err := strconv.ParseUint(vb.String(), 10, va.Type().Bits()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
va.SetUint(f)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeFloat(va, vb reflect.Value) error {
|
||||
switch getKind(vb) {
|
||||
case reflect.Int:
|
||||
va.SetFloat(float64(vb.Int()))
|
||||
case reflect.Uint:
|
||||
va.SetFloat(float64(vb.Uint()))
|
||||
case reflect.Float32:
|
||||
va.Set(vb)
|
||||
case reflect.String:
|
||||
if f, err := strconv.ParseFloat(vb.String(), va.Type().Bits()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
va.SetFloat(f)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getKind(val reflect.Value) reflect.Kind {
|
||||
kind := val.Kind()
|
||||
switch {
|
||||
case kind >= reflect.Int && kind <= reflect.Int64:
|
||||
return reflect.Int
|
||||
case kind >= reflect.Uint && kind <= reflect.Uint64:
|
||||
return reflect.Uint
|
||||
case kind >= reflect.Float32 && kind <= reflect.Float64:
|
||||
return reflect.Float32
|
||||
}
|
||||
return kind
|
||||
}
|
||||
|
||||
func btSplitter(str string) []string {
|
||||
r := bracketSplitter.Split(str, -1)
|
||||
for idx, s := range r {
|
||||
if len(s) == 0 {
|
||||
if len(r) > idx+1 {
|
||||
copy(r[idx:], r[idx+1:])
|
||||
r = r[:len(r)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// queryToMap turns something like a[b][c]=4 into
|
||||
// map[string]interface{}{
|
||||
// "a": map[string]interface{}{
|
||||
// "b": map[string]interface{}{
|
||||
// "c": 4,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
func queryToMap(param string) (map[string]interface{}, error) {
|
||||
rawKey, rawValue, err := splitKeyAndValue(param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawValue, err = url.QueryUnescape(rawValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawKey, err = url.QueryUnescape(rawKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pieces := btSplitter(rawKey)
|
||||
key := pieces[0]
|
||||
|
||||
// If len==1 then rawKey has no [] chars and we can just
|
||||
// decode this as key=value into {key: value}
|
||||
if len(pieces) == 1 {
|
||||
return map[string]interface{}{
|
||||
key: rawValue,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// If len > 1 then we have something like a[b][c]=2
|
||||
// so we need to turn this into {"a": {"b": {"c": 2}}}
|
||||
// To do this we break our key into two pieces:
|
||||
// a and b[c]
|
||||
// and then we set {"a": queryToMap("b[c]", value)}
|
||||
ret := make(map[string]interface{})
|
||||
ret[key], err = queryToMap(buildNewKey(rawKey) + "=" + rawValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// When URL params have a set of empty brackets (eg a[]=1)
|
||||
// it is assumed to be an array. This will get us the
|
||||
// correct value for the array item and return it as an
|
||||
// []interface{} so that it can be merged properly.
|
||||
if pieces[1] == "" {
|
||||
temp := ret[key].(map[string]interface{})
|
||||
ret[key] = []interface{}{temp[""]}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// buildNewKey will take something like:
|
||||
// origKey = "bar[one][two]"
|
||||
// pieces = [bar one two ]
|
||||
// and return "one[two]"
|
||||
func buildNewKey(origKey string) string {
|
||||
pieces := btSplitter(origKey)
|
||||
|
||||
ret := origKey[len(pieces[0])+1:]
|
||||
ret = ret[:len(pieces[1])] + ret[len(pieces[1])+1:]
|
||||
return ret
|
||||
}
|
||||
|
||||
// splitKeyAndValue splits a URL param at the last equal
|
||||
// sign and returns the two strings. If no equal sign is
|
||||
// found, the ErrInvalidParam error is returned.
|
||||
func splitKeyAndValue(param string) (string, string, error) {
|
||||
li := strings.LastIndex(param, "=")
|
||||
if li == -1 {
|
||||
return "", "", ErrInvalidParam
|
||||
}
|
||||
return param[:li], param[li+1:], nil
|
||||
}
|
||||
|
||||
// merge merges a with b if they are either both slices
|
||||
// or map[string]interface{} types. Otherwise it returns b.
|
||||
func merge(a interface{}, b interface{}) interface{} {
|
||||
if av, aok := a.(map[string]interface{}); aok {
|
||||
if bv, bok := b.(map[string]interface{}); bok {
|
||||
return mergeMapIface(av, bv)
|
||||
}
|
||||
}
|
||||
if av, aok := a.([]interface{}); aok {
|
||||
if bv, bok := b.([]interface{}); bok {
|
||||
return mergeSliceIface(av, bv)
|
||||
}
|
||||
}
|
||||
|
||||
va := reflect.ValueOf(a)
|
||||
vb := reflect.ValueOf(b)
|
||||
if (va.Type().Kind() == reflect.Slice) && (va.Type().Elem().Kind() == vb.Type().Kind() || vb.Type().ConvertibleTo(va.Type().Elem())) {
|
||||
va = reflect.Append(va, vb.Convert(va.Type().Elem()))
|
||||
return va.Interface()
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// mergeMap merges a with b, attempting to merge any nested
|
||||
// values in nested maps but eventually overwriting anything
|
||||
// in a that can't be merged with whatever is in b.
|
||||
func mergeMapIface(a map[string]interface{}, b map[string]interface{}) map[string]interface{} {
|
||||
for bK, bV := range b {
|
||||
if aV, ok := a[bK]; ok {
|
||||
if (reflect.ValueOf(aV).Type().Kind() == reflect.ValueOf(bV).Type().Kind()) ||
|
||||
((reflect.ValueOf(aV).Type().Kind() == reflect.Slice) && reflect.ValueOf(aV).Type().Elem().Kind() == reflect.ValueOf(bV).Type().Kind()) {
|
||||
nV := []interface{}{aV, bV}
|
||||
a[bK] = nV
|
||||
} else {
|
||||
a[bK] = merge(a[bK], bV)
|
||||
}
|
||||
} else {
|
||||
a[bK] = bV
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// mergeSlice merges a with b and returns the result.
|
||||
func mergeSliceIface(a []interface{}, b []interface{}) []interface{} {
|
||||
a = append(a, b...)
|
||||
return a
|
||||
}
|
||||
|
45
util/reflect/reflect_test.go
Normal file
45
util/reflect/reflect_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestURLSliceVars(t *testing.T) {
|
||||
u, err := url.Parse("http://localhost/v1/test/call/my_name?key=arg1&key=arg2&key=arg3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err := URLMap(u.RawQuery)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
v, ok := mp["key"]
|
||||
if !ok {
|
||||
t.Fatalf("key not exists: %#+v", mp)
|
||||
}
|
||||
|
||||
vm, ok := v.([]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("invalid key value")
|
||||
}
|
||||
|
||||
if len(vm) != 3 {
|
||||
t.Fatalf("missing key value: %#+v", mp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLVars(t *testing.T) {
|
||||
u, err := url.Parse("http://localhost/v1/test/call/my_name?req=key&arg1=arg1&arg2=12345&nested.string_args=str1&nested.string_args=str2&arg2=54321")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err := URLMap(u.RawQuery)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = mp
|
||||
}
|
Reference in New Issue
Block a user