Skip to content

Commit

Permalink
Use yaml’s Unmarshal func by default (#8)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:

- Drop support for `apollo:"SubstructWithInnerKeyDef.X"`
  • Loading branch information
scorix authored Jul 13, 2023
1 parent 4cee7c8 commit bae0b13
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 217 deletions.
177 changes: 21 additions & 156 deletions oap.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,158 +4,25 @@ import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"

"github.com/philchia/agollo/v4"
"gopkg.in/yaml.v3"
)

type (
UnmarshalFunc = func([]byte, interface{}) error
KindHandler = func(rawtag string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error)
)
type UnmarshalFunc func([]byte, interface{}) error

var (
registryForUnmarshal = make(map[string]UnmarshalFunc)
registryForKindHandler = make(map[reflect.Kind]KindHandler)
)
var registryForUnmarshal = map[string]UnmarshalFunc{
"json": json.Unmarshal,
"yaml": yaml.Unmarshal,
}

// You can use custom unmarshal for strcut type filed.
// Predfined JSON&YAML.
func SetUnmarshalFunc(name string, f UnmarshalFunc) {
registryForUnmarshal[name] = f
}

// You can add your custom process func to relect.Kind type, or support oap not support type.
func SetKindHanlderFunc(kind reflect.Kind, f KindHandler) {
registryForKindHandler[kind] = f
}

func GetUnmarshalFunc(name string) (UnmarshalFunc, bool) {
f, ok := registryForUnmarshal[name]
return f, ok
}

func GetKindHanlderFunc(kind reflect.Kind) (KindHandler, bool) {
f, ok := registryForKindHandler[kind]
return f, ok
}

func init() {
registryForUnmarshal["json"] = json.Unmarshal
registryForUnmarshal["yaml"] = yaml.Unmarshal

registryForKindHandler[reflect.Bool] = boolHandler
registryForKindHandler[reflect.String] = stringHandler
registryForKindHandler[reflect.Int] = intHandler
registryForKindHandler[reflect.Float32] = float32Handler
registryForKindHandler[reflect.Float64] = float64Handler
registryForKindHandler[reflect.Struct] = structHandler
registryForKindHandler[reflect.Ptr] = structHandler
}

func boolHandler(rawtag string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
confV := client.GetString(rawtag, opts...)
filedV := false
if strings.ToLower(confV) == "true" {
filedV = true
}
valueToSet := reflect.ValueOf(filedV)
return &valueToSet, nil
}

func stringHandler(rawtag string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
confV := client.GetString(rawtag, opts...)
valueToSet := reflect.ValueOf(confV)
return &valueToSet, nil
}

func float32Handler(rawtag string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
confV := client.GetString(rawtag, opts...)

var filedV float32

float64V, err := strconv.ParseFloat(confV, 32)
if err != nil {
return nil, err
}
filedV = float32(float64V)

valueToSet := reflect.ValueOf(filedV)
return &valueToSet, nil
}

func float64Handler(rawtag string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
confV := client.GetString(rawtag, opts...)

var filedV float64

float64V, err := strconv.ParseFloat(confV, 32)
if err != nil {
return nil, err
}
filedV = float64V

valueToSet := reflect.ValueOf(filedV)
return &valueToSet, nil
}

func structWithMarhsallHandler(rawtag string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
apolloKeyParts := strings.Split(rawtag, ",")
apolloKey := apolloKeyParts[0]

confV := client.GetString(apolloKey, opts...)

var unmarshalType string
if len(apolloKeyParts) == 2 {
unmarshalType = apolloKeyParts[1]
}
unmarshalFunc, ok := GetUnmarshalFunc(unmarshalType)
if !ok {
return nil, fmt.Errorf("unmarshalType=`%v` from rawtag=`%v` not suported yet", unmarshalType, rawtag)
}
v := reflect.New(expectFieldType)
newP := v.Interface()
if err := unmarshalFunc([]byte(confV), newP); err != nil {
return nil, err
}
valueToSet := reflect.Indirect(reflect.ValueOf(newP))
return &valueToSet, nil
}

func structWithEmptyKeyHandler(rawtag string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
v := reflect.New(expectFieldType)
newP := v.Interface()
if err := Decode(newP, client, make(map[string][]agollo.OpOption)); err != nil {
return nil, err
}
valueToSet := reflect.Indirect(reflect.ValueOf(newP))
return &valueToSet, nil
}

func structHandler(apolloKey string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
if apolloKey == "" {
return structWithEmptyKeyHandler(apolloKey, expectFieldType, client, opts...)
}
return structWithMarhsallHandler(apolloKey, expectFieldType, client, opts...)
}

func intHandler(apolloKey string, expectFieldType reflect.Type, client agollo.Client, opts ...agollo.OpOption) (*reflect.Value, error) {
confV := client.GetString(apolloKey, opts...)

var filedV int

int64V, err := strconv.ParseInt(confV, 10, 64)
if err != nil {
return nil, err
}
filedV = int(int64V)

valueToSet := reflect.ValueOf(filedV)
return &valueToSet, nil
}

func Decode(ptr interface{}, client agollo.Client, keyOpts map[string][]agollo.OpOption) error {
v := reflect.ValueOf(ptr).Elem()
for i := 0; i < v.NumField(); i++ {
Expand All @@ -165,28 +32,26 @@ func Decode(ptr interface{}, client agollo.Client, keyOpts map[string][]agollo.O
apolloKeyParts := strings.Split(apolloRawKey, ",")
apolloKey := apolloKeyParts[0]

// Check struct field type if supported yet
filedTypeKind := structField.Type.Kind()
handler, ok := registryForKindHandler[filedTypeKind]
if !ok {
continue
}
apolloVal := client.GetString(apolloKey, keyOpts[apolloKey]...)
val := reflect.New(structField.Type)

var valueToSetPtr *reflect.Value
var valueToSetErr error
// use unmarshaller function
if len(apolloKeyParts) > 1 {
if unmarshallerFunc, ok := registryForUnmarshal[apolloKeyParts[1]]; ok {
if err := unmarshallerFunc([]byte(apolloVal), val.Interface()); err != nil {
return fmt.Errorf("%s unmarshal %s error: %w", apolloKeyParts[1], apolloKey, err)
}

// use opts if provieded
if opts, ok := keyOpts[apolloKey]; ok {
valueToSetPtr, valueToSetErr = handler(apolloRawKey, structField.Type, client, opts...)
} else {
valueToSetPtr, valueToSetErr = handler(apolloRawKey, structField.Type, client)
v.FieldByName(structField.Name).Set(val.Elem())
}
}
if valueToSetErr != nil {
return valueToSetErr

if err := yaml.Unmarshal([]byte(apolloVal), val.Interface()); err != nil {
return fmt.Errorf("unmarshal %s error: %w", apolloVal, err)
}
// get value from ptr
valueToSet := valueToSetPtr
v.FieldByName(structField.Name).Set(valueToSet.Convert(structField.Type))

v.FieldByName(structField.Name).Set(val.Elem())
}

return nil
}
Loading

0 comments on commit bae0b13

Please sign in to comment.