searcherside/infrastructure/config/yaml.go

116 lines
2.1 KiB
Go

package config
import (
"errors"
"fmt"
"io"
"strings"
"unicode"
"github.com/alecthomas/kong"
"gopkg.in/yaml.v3"
)
type KeyFormatterChain []KeyFormatter
func (c KeyFormatterChain) Replace(in string) string {
for _, f := range c {
in = f.Replace(in)
}
return in
}
type KeyFormatter interface {
Replace(in string) string
}
var KebabToPascalCase KeyFormatter = KeyFormatterFunc(func(in string) string {
inLength := len(in)
out := make([]rune, 0, inLength)
for i := 0; i < inLength; i++ {
if in[i] == '-' {
if i < inLength-1 {
i++
out = append(out, unicode.ToUpper((rune)(in[i])))
}
} else {
out = append(out, (rune)(in[i]))
}
}
return string(out)
})
type Yaml struct {
KeySeparator string
KeyFormatter KeyFormatter
}
func (y Yaml) Loader(r io.Reader) (kong.Resolver, error) {
decoder := yaml.NewDecoder(r)
config := make(map[string]any)
if err := decoder.Decode(&config); err != nil && !errors.Is(err, io.EOF) {
return nil, fmt.Errorf("YAML config decoding error: %w", err)
}
return kong.ResolverFunc(func(context *kong.Context, parent *kong.Path, flag *kong.Flag) (any, error) {
path := strings.Split(flag.Name, y.separator())
if n := parent.Node(); n != nil && n.Type != kong.ApplicationNode {
for _, prefix := range append(n.Aliases, n.Name) {
if val := lookup(config, y.normalize(append([]string{prefix}, path...))); val != nil {
return val, nil
}
}
}
return nil, nil
}), nil
}
func (y Yaml) normalize(path []string) []string {
if y.KeyFormatter != nil {
for i := range path {
path[i] = y.KeyFormatter.Replace(path[i])
}
}
return path
}
func (y Yaml) separator() string {
if y.KeySeparator == "" {
return "."
}
return y.KeySeparator
}
func lookup(config map[string]any, path []string) any {
if len(path) == 0 {
return config
}
if len(path) > 1 {
val, ok := config[path[0]]
if !ok {
return nil
}
if valMap, ok := val.(map[string]any); ok {
return lookup(valMap, path[1:])
} else {
return nil
}
}
return config[path[0]]
}
type KeyFormatterFunc func(in string) string
func (f KeyFormatterFunc) Replace(in string) string {
return f(in)
}