Skip to content

Commit

Permalink
feat(conf): one decoder to decode them all
Browse files Browse the repository at this point in the history
  • Loading branch information
huangliling committed Mar 8, 2023
1 parent 61e373d commit f09b6ea
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 48 deletions.
4 changes: 2 additions & 2 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func (c *ConfigParser[T]) unmarshal(t *T) error {
if c.opts.tagName != "" {
config.TagName = c.opts.tagName
}
}, viper.DecodeHook(decodeHook(c.opts.hooks)))
}, viper.DecodeHook(decodeHook(c.opts.hook)))
}

ty := reflect.TypeOf(*t)
Expand All @@ -249,7 +249,7 @@ func (c *ConfigParser[T]) unmarshal(t *T) error {
if c.opts.tagName != "" {
config.TagName = c.opts.tagName
}
}, viper.DecodeHook(decodeHook(c.opts.hooks)))
}, viper.DecodeHook(decodeHook(c.opts.hook)))
if err != nil {
return err
}
Expand Down
38 changes: 10 additions & 28 deletions conf/decodehook.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,51 +21,33 @@ package conf

import (
"crypto/rsa"
"reflect"
"time"

jwt "github.com/dgrijalva/jwt-go"
"github.com/mitchellh/mapstructure"
"google.golang.org/protobuf/types/known/durationpb"
"reflect"
)

func decodeHook(hooks []DecodeHook) mapstructure.DecodeHookFunc {
func decodeHook(hook DecodeHook) mapstructure.DecodeHookFunc {
return mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
decoder(hooks),
decoder(hook),
)
}

func decoder(hooks []DecodeHook) func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
hm := map[reflect.Type]func(string) (interface{}, error){}
hm[reflect.TypeOf(rsa.PublicKey{})] = func(data string) (interface{}, error) {
return jwt.ParseRSAPublicKeyFromPEM([]byte(data))
}
hm[reflect.TypeOf(rsa.PrivateKey{})] = func(data string) (interface{}, error) {
return jwt.ParseRSAPrivateKeyFromPEM([]byte(data))
}
hm[reflect.TypeOf(durationpb.Duration{})] = func(data string) (interface{}, error) {
t, e := time.ParseDuration(data)
if e != nil {
return nil, e
}
return durationpb.New(t), nil
}
for _, hook := range hooks {
hm[hook.Type] = hook.Decode
}

func decoder(hook DecodeHook) func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
// Check if the data type matches the expected one
if f.Kind() != reflect.String {
return data, nil
}

if h, ok := hm[t]; ok {
return h(data.(string))
switch t {
case reflect.TypeOf(rsa.PublicKey{}):
return jwt.ParseRSAPublicKeyFromPEM([]byte(data.(string)))
case reflect.TypeOf(rsa.PrivateKey{}):
return jwt.ParseRSAPrivateKeyFromPEM([]byte(data.(string)))
}

return data, nil
return hook(t, data.(string))
}
}
15 changes: 15 additions & 0 deletions conf/examples/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package examples_test

import (
"log"
"reflect"

"github.com/antigloss/go/conf"
"github.com/antigloss/go/conf/store"
Expand Down Expand Up @@ -49,6 +50,20 @@ func ExampleConfFromEnv() {
c := conf.New[ExampleConfig](
conf.WithTagName("json"), // Tag name must match with the tag name defined inside the struct for unmarshalling the configurations. Default tag name is mapstructure
conf.WithStores(env.New()), // Create a Store object for reading configurations from ENV
conf.WithDecodeHook(func(to reflect.Type, data string) (interface{}, error) { // Set a decoder to decode user-defined data types
// Decoding implementation goes here
//switch to {
//case reflect.TypeOf(UserDefinedType1{}):
// obj := UserDefinedType1{}
// err := obj.Parse(data)
// return obj, err
//case reflect.TypeOf(UserDefinedType2{}):
// obj := UserDefinedType2{}
// err := obj.Parse(data)
// return obj, err
//}
return data, nil
}),
)

bc, err := c.Parse()
Expand Down
19 changes: 7 additions & 12 deletions conf/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,14 @@ func WithTagName(tag string) option {
}
}

// DecodeHook decoder for a specified data type
type DecodeHook struct {
// Data type
Type reflect.Type
// Decoder for decoding raw configuration data into `Type`.
// It returns the decoded value as interface{} on success, otherwise, an error is returned.
Decode func(data string) (interface{}, error)
}
// DecodeHook decodes `data` to an object of type `to`.
// It returns the decoded value as interface{} on success, otherwise, an error is returned.
type DecodeHook func(to reflect.Type, data string) (interface{}, error)

// WithDecodeHooks sets user-defined decoders
func WithDecodeHooks(hooks ...DecodeHook) option {
// WithDecodeHook sets a user-defined decoder
func WithDecodeHook(hook DecodeHook) option {
return func(o *options) {
o.hooks = hooks
o.hook = hook
}
}

Expand All @@ -60,7 +55,7 @@ type option func(opts *options)
type options struct {
stores []store.Store
tagName string
hooks []DecodeHook
hook DecodeHook
}

func (o *options) apply(opts ...option) {
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
github.com/spf13/viper v1.14.0
github.com/taptap/go-apollo v1.3.0
golang.org/x/exp v0.0.0-20230111222715-75897c7a292a
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v3 v3.0.1
)

Expand Down
5 changes: 0 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
Expand All @@ -130,7 +129,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
Expand Down Expand Up @@ -597,9 +595,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
Expand Down

0 comments on commit f09b6ea

Please sign in to comment.