Files
Archduino/internal/config/config.go

142 lines
3.8 KiB
Go

// 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
type Packages struct {
Base []string `yaml:"base"`
}
type ArduinoCLIConfig struct {
Version string `yaml:"version"`
URL string `yaml:"url"`
Cores []string `yaml:"cores"`
}
type ZshPlugin struct {
Name string `yaml:"name"`
Repo string `yaml:"repo"`
}
type ZshConfig struct {
OhMyZshRepo string `yaml:"oh_my_zsh_repo"`
Plugins []ZshPlugin `yaml:"plugins"`
}
type ZellijConfig struct {
ConfigURL string `yaml:"config_url"`
}
type ThemesConfig struct {
Source string `yaml:"source"`
}
type ShellConfigBlock struct {
Name string `yaml:"name"`
File string `yaml:"file"`
Content string `yaml:"content"`
}
type ShellConfigSection struct {
Blocks []ShellConfigBlock `yaml:"blocks"`
}
// 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"`
Packages Packages `yaml:"packages"`
Fonts []string `yaml:"fonts"`
ArduinoCLI ArduinoCLIConfig `yaml:"arduino_cli"`
Zsh ZshConfig `yaml:"zsh"`
Zellij ZellijConfig `yaml:"zellij"`
Themes ThemesConfig `yaml:"themes"`
ShellConfig ShellConfigSection `yaml:"shell_config"`
}
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
}
if len(over.Packages.Base) > 0 {
out.Packages = over.Packages
}
if len(over.Fonts) > 0 {
out.Fonts = over.Fonts
}
if over.ArduinoCLI.Version != "" {
out.ArduinoCLI = over.ArduinoCLI
}
if over.Zsh.OhMyZshRepo != "" {
out.Zsh = over.Zsh
}
if over.Zellij.ConfigURL != "" {
out.Zellij = over.Zellij
}
if over.Themes.Source != "" {
out.Themes = over.Themes
}
if len(over.ShellConfig.Blocks) > 0 {
out.ShellConfig = over.ShellConfig
}
return out
}