feat(config): add reflection-based configFields framework
This commit is contained in:
parent
55b2e7f6cb
commit
3f6ddfbcd4
2 changed files with 193 additions and 0 deletions
55
cmd/config_fields.go
Normal file
55
cmd/config_fields.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package cmd
|
||||
|
||||
import "reflect"
|
||||
|
||||
// ConfigField represents a single leaf field in the Config struct,
|
||||
// mapped to its dotted TOML key path.
|
||||
type ConfigField struct {
|
||||
Key string // dotted key, e.g. "git.auto_commit"
|
||||
Value any // current value
|
||||
Default any // value from defaultConfig()
|
||||
IsDefault bool // Value == Default
|
||||
Field reflect.Value // settable reflect.Value (from cfg pointer)
|
||||
Kind reflect.Kind // field type kind
|
||||
}
|
||||
|
||||
// configFields walks cfg and defaults in parallel, returning a ConfigField
|
||||
// for every leaf field. Keys are built from TOML struct tags.
|
||||
func configFields(cfg, defaults *Config) []ConfigField {
|
||||
var fields []ConfigField
|
||||
walk(reflect.ValueOf(cfg).Elem(), reflect.ValueOf(defaults).Elem(), "", &fields)
|
||||
return fields
|
||||
}
|
||||
|
||||
func walk(cv, dv reflect.Value, prefix string, out *[]ConfigField) {
|
||||
ct := cv.Type()
|
||||
for i := 0; i < ct.NumField(); i++ {
|
||||
sf := ct.Field(i)
|
||||
tag := sf.Tag.Get("toml")
|
||||
if tag == "" || tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
key := tag
|
||||
if prefix != "" {
|
||||
key = prefix + "." + tag
|
||||
}
|
||||
|
||||
cfv := cv.Field(i)
|
||||
dfv := dv.Field(i)
|
||||
|
||||
if sf.Type.Kind() == reflect.Struct {
|
||||
walk(cfv, dfv, key, out)
|
||||
continue
|
||||
}
|
||||
|
||||
*out = append(*out, ConfigField{
|
||||
Key: key,
|
||||
Value: cfv.Interface(),
|
||||
Default: dfv.Interface(),
|
||||
IsDefault: reflect.DeepEqual(cfv.Interface(), dfv.Interface()),
|
||||
Field: cfv,
|
||||
Kind: sf.Type.Kind(),
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue