feat(go-config): multi-format codecs, envc CLI, fixtures, ASRs
- Module github.com/eslider/go-config with env/yaml/json/ini packages - internal: source, keymap, merge, structconv, bytesutil - cmd/envc: convert, get, merge - docs/asr ASR-0001..0008, README/CHANGELOG/AGENTS refresh - golangci-lint v2 config; tests + fixtures Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// Package keymap normalizes map keys recursively for cross-format matching.
|
||||
package keymap
|
||||
|
||||
import "strings"
|
||||
|
||||
// Normalizer transforms a single path segment or key string.
|
||||
type Normalizer func(string) string
|
||||
|
||||
// LowerAlnum lowercases and strips any rune that is not [a-z0-9].
|
||||
func LowerAlnum(s string) string {
|
||||
var b strings.Builder
|
||||
b.Grow(len(s))
|
||||
for _, r := range strings.ToLower(s) {
|
||||
if r >= 'a' && r <= 'z' || r >= '0' && r <= '9' {
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Identity returns s unchanged.
|
||||
func Identity(s string) string { return s }
|
||||
|
||||
// Walk recursively rewrites every map key in place using n. Slices of maps are walked.
|
||||
func Walk(m map[string]any, n Normalizer) {
|
||||
if m == nil || n == nil {
|
||||
return
|
||||
}
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
for _, k := range keys {
|
||||
v := m[k]
|
||||
delete(m, k)
|
||||
nk := n(k)
|
||||
if nk == "" {
|
||||
nk = k
|
||||
}
|
||||
switch vv := v.(type) {
|
||||
case map[string]any:
|
||||
Walk(vv, n)
|
||||
m[nk] = vv
|
||||
case []any:
|
||||
for i := range vv {
|
||||
if sm, ok := vv[i].(map[string]any); ok {
|
||||
Walk(sm, n)
|
||||
vv[i] = sm
|
||||
}
|
||||
}
|
||||
m[nk] = vv
|
||||
default:
|
||||
m[nk] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package keymap
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLowerAlnum(t *testing.T) {
|
||||
if got, want := LowerAlnum("sub-Service"), "subservice"; got != want {
|
||||
t.Fatalf("got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalk(t *testing.T) {
|
||||
m := map[string]any{
|
||||
"Sub-Service": map[string]any{"Pool-Size": "10"},
|
||||
}
|
||||
Walk(m, LowerAlnum)
|
||||
want := map[string]any{
|
||||
"subservice": map[string]any{"poolsize": "10"},
|
||||
}
|
||||
if !reflect.DeepEqual(m, want) {
|
||||
t.Fatalf("got %#v want %#v", m, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user