codec: add ability to control codec via struct tags
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
parent
ba8e1889fe
commit
34f0b209cc
@ -28,6 +28,8 @@ var (
|
|||||||
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
|
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
|
||||||
// DefaultCodec is the global default codec
|
// DefaultCodec is the global default codec
|
||||||
DefaultCodec Codec = NewCodec()
|
DefaultCodec Codec = NewCodec()
|
||||||
|
// DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal
|
||||||
|
DefaultTagName = "codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageType specifies message type for codec
|
// MessageType specifies message type for codec
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
ErrInvalidStruct = errors.New("invalid struct specified")
|
ErrInvalidStruct = errors.New("invalid struct specified")
|
||||||
ErrInvalidValue = errors.New("invalid value specified")
|
ErrInvalidValue = errors.New("invalid value specified")
|
||||||
|
ErrNotFound = errors.New("struct field not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Option func(*Options)
|
type Option func(*Options)
|
||||||
|
@ -14,6 +14,84 @@ var ErrInvalidParam = errors.New("invalid url query param provided")
|
|||||||
|
|
||||||
var bracketSplitter = regexp.MustCompile(`\[|\]`)
|
var bracketSplitter = regexp.MustCompile(`\[|\]`)
|
||||||
|
|
||||||
|
func StructFieldByTag(src interface{}, tkey string, tval string) (interface{}, error) {
|
||||||
|
sv := reflect.ValueOf(src)
|
||||||
|
if sv.Kind() == reflect.Ptr {
|
||||||
|
sv = sv.Elem()
|
||||||
|
}
|
||||||
|
if sv.Kind() != reflect.Struct {
|
||||||
|
return nil, ErrInvalidStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := sv.Type()
|
||||||
|
for idx := 0; idx < typ.NumField(); idx++ {
|
||||||
|
fld := typ.Field(idx)
|
||||||
|
val := sv.Field(idx)
|
||||||
|
if !val.CanSet() || len(fld.PkgPath) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch val.Kind() {
|
||||||
|
default:
|
||||||
|
ts, ok := fld.Tag.Lookup(tkey)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, p := range strings.Split(ts, ",") {
|
||||||
|
if p == tval {
|
||||||
|
return val.Interface(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
if val = val.Elem(); val.Kind() == reflect.Struct {
|
||||||
|
if iface, err := StructFieldByTag(val.Interface(), tkey, tval); err == nil {
|
||||||
|
return iface, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
if iface, err := StructFieldByTag(val.Interface(), tkey, tval); err == nil {
|
||||||
|
return iface, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func StructFieldByName(src interface{}, tkey string) (interface{}, error) {
|
||||||
|
sv := reflect.ValueOf(src)
|
||||||
|
if sv.Kind() == reflect.Ptr {
|
||||||
|
sv = sv.Elem()
|
||||||
|
}
|
||||||
|
if sv.Kind() != reflect.Struct {
|
||||||
|
return nil, ErrInvalidStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := sv.Type()
|
||||||
|
for idx := 0; idx < typ.NumField(); idx++ {
|
||||||
|
fld := typ.Field(idx)
|
||||||
|
val := sv.Field(idx)
|
||||||
|
if !val.CanSet() || len(fld.PkgPath) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch val.Kind() {
|
||||||
|
default:
|
||||||
|
if fld.Name == tkey {
|
||||||
|
return val.Interface(), nil
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
if val = val.Elem(); val.Kind() == reflect.Struct {
|
||||||
|
if iface, err := StructFieldByName(val.Interface(), tkey); err == nil {
|
||||||
|
return iface, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
if iface, err := StructFieldByName(val.Interface(), tkey); err == nil {
|
||||||
|
return iface, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// StructFields returns slice of struct fields
|
// StructFields returns slice of struct fields
|
||||||
func StructFields(src interface{}) ([]reflect.StructField, error) {
|
func StructFields(src interface{}) ([]reflect.StructField, error) {
|
||||||
var fields []reflect.StructField
|
var fields []reflect.StructField
|
||||||
|
@ -5,6 +5,44 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestStructByTag(t *testing.T) {
|
||||||
|
type Str struct {
|
||||||
|
Name []string `json:"name" codec:"flatten"`
|
||||||
|
}
|
||||||
|
|
||||||
|
val := &Str{Name: []string{"first", "second"}}
|
||||||
|
|
||||||
|
iface, err := StructFieldByTag(val, "codec", "flatten")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := iface.([]string); !ok {
|
||||||
|
t.Fatalf("not []string %v", iface)
|
||||||
|
} else if len(v) != 2 {
|
||||||
|
t.Fatalf("invalid number %v", iface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStructByName(t *testing.T) {
|
||||||
|
type Str struct {
|
||||||
|
Name []string `json:"name" codec:"flatten"`
|
||||||
|
}
|
||||||
|
|
||||||
|
val := &Str{Name: []string{"first", "second"}}
|
||||||
|
|
||||||
|
iface, err := StructFieldByName(val, "Name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := iface.([]string); !ok {
|
||||||
|
t.Fatalf("not []string %v", iface)
|
||||||
|
} else if len(v) != 2 {
|
||||||
|
t.Fatalf("invalid number %v", iface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStructURLValues(t *testing.T) {
|
func TestStructURLValues(t *testing.T) {
|
||||||
type Str struct {
|
type Str struct {
|
||||||
Str *Str `json:"str"`
|
Str *Str `json:"str"`
|
||||||
|
Loading…
Reference in New Issue
Block a user