- LoadWithSources: reflection snapshots at layer boundaries (default -> env-file per file -> process-env incl. DSN/CORS/KB normalization); diff yields exact per-field source - no manual key registry, no drift - Load unchanged signature/behavior (wrapper); ApplyCLIOverlay records the cli layer on a cloned config - 'config explain' upgraded to field table (59 fields; source + set flag only, values never printed per KTD17) - replaces the reporting-only key registry from PR #12 - tests: layer attribution (file/process override), cli overlay, Load regression
53 lines
1.3 KiB
Go
53 lines
1.3 KiB
Go
package config
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// ParseEnvFileKeys 用与 applyEnvFile 相同的解析规则列出文件中出现的键。
|
|
func ParseEnvFileKeys(path string) ([]string, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
var keys []string
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(line, "=", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
keys = append(keys, strings.TrimSpace(parts[0]))
|
|
}
|
|
return keys, scanner.Err()
|
|
}
|
|
|
|
// FormatFieldSources 渲染字段级来源表(KTD17:只报告来源/状态,不打印值)。
|
|
func FormatFieldSources(entries []FieldSource) string {
|
|
var b strings.Builder
|
|
b.WriteString("field layer set\n")
|
|
for _, e := range entries {
|
|
fmt.Fprintf(&b, "%-37s %-19s %v\n", e.Field, e.Layer, e.Set)
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// SortedFieldSourceNames 返回已知来源字段名(测试辅助)。
|
|
func SortedFieldSourceNames(m map[string]FieldSource) []string {
|
|
names := make([]string, 0, len(m))
|
|
for k := range m {
|
|
names = append(names, k)
|
|
}
|
|
sort.Strings(names)
|
|
return names
|
|
}
|