feat: load explicit configuration references and remove implicit env overlays

This commit is contained in:
2026-09-26 16:14:54 +08:00
parent 4bf6e88cfe
commit d598b2ebee
94 changed files with 1355 additions and 1759 deletions

View File

@@ -59,6 +59,20 @@ func (b *documentBinder) bind(v any, dst reflect.Value, path string, depth int,
if err := b.step(path, depth); err != nil {
return err
}
if text, ok := v.(resolvedText); ok {
if dst.Kind() != reflect.Pointer && dst.Type() != durationType && dst.CanAddr() && !dst.Addr().Type().Implements(jsonUnmarshalerType) && !dst.Addr().Type().Implements(textType) {
switch dst.Kind() {
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
if err := parseTextValue(dst, string(text)); err != nil {
return b.doc.diagnostic(path, "expected a valid "+dst.Type().String(), err)
}
return nil
}
}
if dst.Kind() != reflect.Pointer {
v = string(text)
}
}
if v == nil {
return b.decode(nil, dst, path)
} // Preserve encoding/json null semantics.
@@ -78,7 +92,7 @@ func (b *documentBinder) bind(v any, dst reflect.Value, path string, depth int,
default:
return b.decode(v, dst, path)
}
if err := parseEnvValue(dst, strings.TrimSpace(raw)); err != nil {
if err := parseTextValue(dst, strings.TrimSpace(raw)); err != nil {
return b.doc.diagnostic(path, "expected a duration such as 5s", err)
}
return nil
@@ -102,7 +116,7 @@ func (b *documentBinder) bind(v any, dst reflect.Value, path string, depth int,
}
switch dst.Kind() {
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
if err := parseEnvValue(dst, strings.TrimSpace(string(text))); err != nil {
if err := parseTextValue(dst, strings.TrimSpace(string(text))); err != nil {
return b.doc.diagnostic(path, "expected a valid "+dst.Type().String(), err)
}
return nil
@@ -143,7 +157,7 @@ func (b *documentBinder) bind(v any, dst reflect.Value, path string, depth int,
}
}
if !ok {
return b.doc.diagnostic(child, "unknown field", nil)
return b.doc.diagnostic(child, unknownFieldReason(name, fields), nil)
}
if used[canonical] {
return b.doc.diagnostic(child, "duplicate field binding", nil)
@@ -237,6 +251,8 @@ func (b *documentBinder) dynamic(v any, path string, depth int) (any, error) {
switch x := v.(type) {
case Text:
return string(x), nil
case resolvedText:
return string(x), nil
case map[string]any:
out := make(map[string]any, len(x))
for _, k := range sortedKeys(x) {