diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-16 03:51:43 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-16 03:51:43 +0200 |
| commit | de3e878ad12bbd3e609bd5b7d741fc792c72f255 (patch) | |
| tree | 06d92b93ea0ad532c5d3a761033baac05abe2a5e /internal/runtimeconfig/store.go | |
| parent | 2e9cabb1c8bf1f0246e513fe1f86a552e07eee94 (diff) | |
Decompose App God struct into embedded section structs
Replace 60+ flat fields in App with 4 embedded section structs:
CoreConfig, ProviderConfig, PromptConfig, FeatureConfig. Go field
promotion preserves all existing field access patterns. Updated
flattenAppConfig to recurse into embedded structs for runtimeconfig.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/runtimeconfig/store.go')
| -rw-r--r-- | internal/runtimeconfig/store.go | 66 |
1 files changed, 44 insertions, 22 deletions
diff --git a/internal/runtimeconfig/store.go b/internal/runtimeconfig/store.go index 4ee7ada..b8d34b4 100644 --- a/internal/runtimeconfig/store.go +++ b/internal/runtimeconfig/store.go @@ -118,38 +118,60 @@ func Diff(oldCfg, newCfg appconfig.App) []Change { return changes } +// flattenAppConfig converts an App config into a flat key/value map for diffing. +// It recurses into embedded structs (CoreConfig, ProviderConfig, etc.) to reach +// all leaf fields. Keys are derived from json tags, with fallbacks for fields +// that use json:"-" (e.g. surface configs, stats). func flattenAppConfig(cfg appconfig.App) map[string]string { result := make(map[string]string) - val := reflect.ValueOf(cfg) + flattenStructFields(reflect.ValueOf(cfg), result) + return result +} + +// flattenStructFields iterates over struct fields, recursing into anonymous +// (embedded) structs and extracting key/value pairs from leaf fields. +func flattenStructFields(val reflect.Value, result map[string]string) { typ := val.Type() for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) - key := strings.TrimSpace(field.Tag.Get("toml")) - if key == "" || key == "-" { - switch field.Name { - case "StatsWindowMinutes": - key = "stats_window_minutes" - case "CompletionConfigs": - key = "completion_configs" - case "CodeActionConfigs": - key = "code_action_configs" - case "ChatConfigs": - key = "chat_configs" - case "CLIConfigs": - key = "cli_configs" - default: - continue - } - } - if idx := strings.Index(key, ","); idx >= 0 { - key = key[:idx] + // Recurse into embedded (anonymous) structs to flatten their fields. + if field.Anonymous && field.Type.Kind() == reflect.Struct { + flattenStructFields(val.Field(i), result) + continue } - if key == "" || key == "-" { + key := fieldKey(field) + if key == "" { continue } result[key] = stringifyValue(val.Field(i)) } - return result +} + +// fieldKey derives the flattened map key for a struct field from its json tag, +// with manual fallbacks for fields tagged json:"-" that still need tracking. +func fieldKey(field reflect.StructField) string { + key := strings.TrimSpace(field.Tag.Get("json")) + if key == "" || key == "-" { + // Manual fallbacks for fields hidden from JSON but needed in diffs. + switch field.Name { + case "StatsWindowMinutes": + return "stats_window_minutes" + case "CompletionConfigs": + return "completion_configs" + case "CodeActionConfigs": + return "code_action_configs" + case "ChatConfigs": + return "chat_configs" + case "CLIConfigs": + return "cli_configs" + default: + return "" + } + } + if idx := strings.Index(key, ","); idx >= 0 { + key = key[:idx] + } + return key } func stringifyValue(v reflect.Value) string { |
