package config import ( "fmt" "os" "strings" "github.com/go-kratos/kratos/v2/encoding" "github.com/go-kratos/kratos/v2/log" ) // Decoder is config decoder. type Decoder func(*KeyValue, map[string]interface{}) error // Resolver resolve placeholder in config. type Resolver func(map[string]interface{}) error // Option is config option. type Option func(*options) type options struct { sources []Source decoder Decoder resolver Resolver logger log.Logger } // WithSource with config source. func WithSource(s ...Source) Option { return func(o *options) { o.sources = s } } // WithDecoder with config decoder. func WithDecoder(d Decoder) Option { return func(o *options) { o.decoder = d } } // WithResolver with config resolver. func WithResolver(r Resolver) Option { return func(o *options) { o.resolver = r } } // WithLogger with config logger. func WithLogger(l log.Logger) Option { return func(o *options) { o.logger = l } } // defaultDecoder decode config from source KeyValue // to target map[string]interface{} using src.Format codec. func defaultDecoder(src *KeyValue, target map[string]interface{}) error { if src.Format == "" { target[src.Key] = src.Value return nil } if codec := encoding.GetCodec(src.Format); codec != nil { return codec.Unmarshal(src.Value, &target) } return fmt.Errorf("unsupported key: %s format: %s", src.Key, src.Format) } // defaultResolver resolve placeholder in map value, // placeholder format in ${key:default} or $key. func defaultResolver(input map[string]interface{}) error { mapper := func(name string) string { args := strings.Split(strings.TrimSpace(name), ":") if v, has := readValue(input, args[0]); has { s, _ := v.String() return s } else if len(args) > 1 { // default value return args[1] } return "" } var resolve func(map[string]interface{}) error resolve = func(sub map[string]interface{}) error { for k, v := range sub { switch vt := v.(type) { case string: sub[k] = os.Expand(vt, mapper) case map[string]interface{}: if err := resolve(vt); err != nil { return err } case []interface{}: for i, iface := range vt { switch it := iface.(type) { case string: vt[i] = os.Expand(it, mapper) case map[string]interface{}: if err := resolve(it); err != nil { return err } } } sub[k] = vt } } return nil } return resolve(input) }