// Package config loads embedded defaults and optionally merges a user // overlay from ~/.config/archduino/overlay.yaml. package config import ( _ "embed" "fmt" "os" "path/filepath" "gopkg.in/yaml.v3" ) //go:embed defaults.yaml var defaultsYAML []byte // Config is the fully-resolved runtime configuration. type Config struct { Version int `yaml:"version"` ExtraPackages []string `yaml:"extra_packages"` DisabledModules []string `yaml:"disabled_modules"` ThemeOverrides map[string]string `yaml:"theme_overrides"` } const currentVersion = 1 // Load returns defaults merged with the overlay at overlayPath (if present). // If overlayPath is "", the default location (~/.config/archduino/overlay.yaml) // is used. A missing overlay is not an error. func Load(overlayPath string) (Config, error) { var cfg Config if err := yaml.Unmarshal(defaultsYAML, &cfg); err != nil { return Config{}, fmt.Errorf("parse embedded defaults: %w", err) } if overlayPath == "" { home, err := os.UserHomeDir() if err != nil { return cfg, nil } overlayPath = filepath.Join(home, ".config", "archduino", "overlay.yaml") } data, err := os.ReadFile(overlayPath) if err != nil { if os.IsNotExist(err) { return cfg, nil } return Config{}, fmt.Errorf("read overlay %s: %w", overlayPath, err) } var overlay Config if err := yaml.Unmarshal(data, &overlay); err != nil { return Config{}, fmt.Errorf("parse overlay %s: %w", overlayPath, err) } if overlay.Version != 0 && overlay.Version != currentVersion { return Config{}, fmt.Errorf("overlay %s: unsupported version %d (want %d)", overlayPath, overlay.Version, currentVersion) } return merge(cfg, overlay), nil } func merge(base, over Config) Config { out := base if len(over.ExtraPackages) > 0 { out.ExtraPackages = append(append([]string{}, base.ExtraPackages...), over.ExtraPackages...) } if len(over.DisabledModules) > 0 { out.DisabledModules = append(append([]string{}, base.DisabledModules...), over.DisabledModules...) } if len(over.ThemeOverrides) > 0 { merged := make(map[string]string, len(base.ThemeOverrides)+len(over.ThemeOverrides)) for k, v := range base.ThemeOverrides { merged[k] = v } for k, v := range over.ThemeOverrides { merged[k] = v } out.ThemeOverrides = merged } return out }