Skip to content

Commit

Permalink
fix: parsing config input and convert to match the type
Browse files Browse the repository at this point in the history
  • Loading branch information
akoserwal committed May 2, 2024
1 parent b3b2a34 commit 48bfca4
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 15 deletions.
91 changes: 77 additions & 14 deletions config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"fmt"
"regexp"
"strconv"
"strings"

"github.com/go-kratos/kratos/v2/encoding"
Expand Down Expand Up @@ -45,6 +46,14 @@ func WithDecoder(d Decoder) Option {
}
}

// WithNewResolver with config resolver.
// bool input will enable conversion of config to data types
func WithNewResolver(enableConvertToType bool) Option {
return func(o *options) {
o.resolver = newDefaultResolver(enableConvertToType)
}
}

// WithResolver with config resolver.
func WithResolver(r Resolver) Option {
return func(o *options) {
Expand Down Expand Up @@ -82,26 +91,31 @@ func defaultDecoder(src *KeyValue, target map[string]interface{}) error {
return fmt.Errorf("unsupported key: %s format: %s", src.Key, src.Format)
}

func newDefaultResolver(enableConvertToType bool) func(map[string]interface{}) error {
return func(input map[string]interface{}) error {
mapper := mapper(input)
err := resolver(input, mapper, enableConvertToType)
if err != nil {
return err
}
return nil
}
}

// defaultResolver resolve placeholder in map value,
// placeholder format in ${key:default}.
func defaultResolver(input map[string]interface{}) error {
mapper := func(name string) string {
args := strings.SplitN(strings.TrimSpace(name), ":", 2) //nolint:gomnd
if v, has := readValue(input, args[0]); has {
s, _ := v.String()
return s
} else if len(args) > 1 { // default value
return args[1]
}
return ""
}
mapper := mapper(input)
return resolver(input, mapper, false)
}

func resolver(input map[string]interface{}, mapper func(name string) string, toType bool) error {
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] = expand(vt, mapper)
sub[k] = expand(vt, mapper, toType)
case map[string]interface{}:
if err := resolve(vt); err != nil {
return err
Expand All @@ -110,7 +124,7 @@ func defaultResolver(input map[string]interface{}) error {
for i, iface := range vt {
switch it := iface.(type) {
case string:
vt[i] = expand(it, mapper)
vt[i] = expand(it, mapper, toType)
case map[string]interface{}:
if err := resolve(it); err != nil {
return err
Expand All @@ -125,12 +139,61 @@ func defaultResolver(input map[string]interface{}) error {
return resolve(input)
}

func expand(s string, mapping func(string) string) string {
func mapper(input map[string]interface{}) func(name string) string {
mapper := func(name string) string {
args := strings.SplitN(strings.TrimSpace(name), ":", 2) //nolint:gomnd
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) interface{} {
// 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
}

Check failure on line 183 in config/options.go

View workflow job for this annotation

GitHub Actions / lint module (.)

File is not `gofumpt`-ed (gofumpt)
func expand(s string, mapping func(string) string, toType bool) interface{} {
r := regexp.MustCompile(`\${(.*?)}`)
re := r.FindAllStringSubmatch(s, -1)
var ct interface{}
for _, i := range re {
if len(i) == 2 { //nolint:gomnd
s = strings.ReplaceAll(s, i[0], mapping(i[1]))
m := mapping(i[1])
if toType {
ct = convertToType(m)
return ct
} else {

Check warning on line 194 in config/options.go

View workflow job for this annotation

GitHub Actions / lint module (.)

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)
s = strings.ReplaceAll(s, i[0], m)
}
}
}
return s
Expand Down
2 changes: 1 addition & 1 deletion config/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func TestExpand(t *testing.T) {
},
}
for _, tt := range tests {
if got := expand(tt.input, tt.mapping); got != tt.want {
if got := expand(tt.input, tt.mapping, false); got != tt.want {
t.Errorf("expand() want: %s, got: %s", tt.want, got)
}
}
Expand Down

0 comments on commit 48bfca4

Please sign in to comment.