feat(infra): tipos ConfigError y ConfigValidation + funciones puras Go (validate, merge, dump)
- ConfigError y ConfigValidation como tipos producto con sus .md en types/infra/ - config_validate: validacion con tags required/format/min/max/oneof via reflection - config_merge: merge no-mutante de map[string]string con precedencia de override - config_dump: serializacion de structs a map con mascara *** para campos secret - 17 tests unitarios, todos PASS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const secretMask = "***"
|
||||
|
||||
// ConfigDump converts a configuration struct to a map[string]string.
|
||||
// Exported fields are included. Fields tagged with secret:"true" have their
|
||||
// value replaced with "***". Nested structs are not traversed — only top-level
|
||||
// exported fields are included.
|
||||
//
|
||||
// The map key is the field name. Values are formatted with fmt.Sprintf("%v", ...).
|
||||
func ConfigDump(cfg any) map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
v := reflect.ValueOf(cfg)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return result
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Struct {
|
||||
return result
|
||||
}
|
||||
|
||||
t := v.Type()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
key := field.Name
|
||||
|
||||
if field.Tag.Get("secret") == "true" {
|
||||
result[key] = secretMask
|
||||
continue
|
||||
}
|
||||
|
||||
fv := v.Field(i)
|
||||
result[key] = fmt.Sprintf("%v", fv.Interface())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user