1
0
mirror of https://github.com/go-kratos/kratos.git synced 2025-10-30 23:47:59 +02:00
Files
kratos/config/options.go
Sasha Melentyev 7a9a72e951 chore: replace interface{} with any (#3557)
Signed-off-by: Sasha Melentyev <sasha@m8.ru>
2025-03-07 23:56:30 +08:00

197 lines
4.6 KiB
Go

package config
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/go-kratos/kratos/v2/encoding"
)
// Decoder is config decoder.
type Decoder func(*KeyValue, map[string]any) error
// Resolver resolve placeholder in config.
type Resolver func(map[string]any) error
// Merge is config merge func.
type Merge func(dst, src any) error
// Option is config option.
type Option func(*options)
type options struct {
sources []Source
decoder Decoder
resolver Resolver
merge Merge
}
// WithSource with config source.
func WithSource(s ...Source) Option {
return func(o *options) {
o.sources = s
}
}
// WithDecoder with config decoder.
// DefaultDecoder behavior:
// If KeyValue.Format is non-empty, then KeyValue.Value will be deserialized into map[string]interface{}
// and stored in the config cache(map[string]interface{})
// if KeyValue.Format is empty,{KeyValue.Key : KeyValue.Value} will be stored in config cache(map[string]interface{})
func WithDecoder(d Decoder) Option {
return func(o *options) {
o.decoder = d
}
}
// WithResolveActualTypes with config resolver.
// bool input will enable conversion of config to data types
func WithResolveActualTypes(enableConvertToType bool) Option {
return func(o *options) {
o.resolver = newActualTypesResolver(enableConvertToType)
}
}
// WithResolver with config resolver.
func WithResolver(r Resolver) Option {
return func(o *options) {
o.resolver = r
}
}
// WithMergeFunc with config merge func.
func WithMergeFunc(m Merge) Option {
return func(o *options) {
o.merge = m
}
}
// defaultDecoder decode config from source KeyValue
// to target map[string]interface{} using src.Format codec.
func defaultDecoder(src *KeyValue, target map[string]any) error {
if src.Format == "" {
// expand key "aaa.bbb" into map[aaa]map[bbb]interface{}
keys := strings.Split(src.Key, ".")
for i, k := range keys {
if i == len(keys)-1 {
target[k] = src.Value
} else {
sub := make(map[string]any)
target[k] = sub
target = sub
}
}
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)
}
func newActualTypesResolver(enableConvertToType bool) func(map[string]any) error {
return func(input map[string]any) error {
mapper := mapper(input)
return resolver(input, mapper, enableConvertToType)
}
}
// defaultResolver resolve placeholder in map value,
// placeholder format in ${key:default}.
func defaultResolver(input map[string]any) error {
mapper := mapper(input)
return resolver(input, mapper, false)
}
func resolver(input map[string]any, mapper func(name string) string, toType bool) error {
var resolve func(map[string]any) error
resolve = func(sub map[string]any) error {
for k, v := range sub {
switch vt := v.(type) {
case string:
sub[k] = expand(vt, mapper, toType)
case map[string]any:
if err := resolve(vt); err != nil {
return err
}
case []any:
for i, iface := range vt {
switch it := iface.(type) {
case string:
vt[i] = expand(it, mapper, toType)
case map[string]any:
if err := resolve(it); err != nil {
return err
}
}
}
sub[k] = vt
}
}
return nil
}
return resolve(input)
}
func mapper(input map[string]any) func(name string) string {
mapper := func(name string) string {
args := strings.SplitN(strings.TrimSpace(name), ":", 2) //nolint:mnd
if v, has := readValue(input, args[0]); has {
s, _ := v.String()
return s
} else if len(args) > 1 { // default value
return args[1]
}
return ""
}
return mapper
}
func convertToType(input string) any {
// Check if the input is a string with quotes
if strings.HasPrefix(input, "\"") && strings.HasSuffix(input, "\"") {
// Trim the quotes and return the string value
return strings.Trim(input, "\"")
}
// Try converting to bool
if input == "true" || input == "false" {
b, _ := strconv.ParseBool(input)
return b
}
// Try converting to float64
if strings.Contains(input, ".") {
if f, err := strconv.ParseFloat(input, 64); err == nil {
return f
}
}
// Try converting to int64
if i, err := strconv.ParseInt(input, 10, 64); err == nil {
return i
}
// Default to string if no other conversion succeeds
return input
}
func expand(s string, mapping func(string) string, toType bool) any {
r := regexp.MustCompile(`\${(.*?)}`)
re := r.FindAllStringSubmatch(s, -1)
var ct any
for _, i := range re {
if len(i) == 2 { //nolint:mnd
m := mapping(i[1])
if toType {
ct = convertToType(m)
return ct
}
s = strings.ReplaceAll(s, i[0], m)
}
}
return s
}