116 lines
2.1 KiB
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)
|
|
}
|