package config import ( "errors" "fmt" "os" "path/filepath" "strconv" "strings" "github.com/BurntSushi/toml" "sectorinf.com/emilis/hlctl/ctllog" "sectorinf.com/emilis/hlctl/hlcl/cmd" ) var ( cfgPath = filepath.Join( os.Getenv("HOME"), ".config", "herbstluftwm", "hlctl.toml", ) log = ctllog.Logger{}.New("config", ctllog.ColorRGB{}.FromHex("ff5aff")) ) type ( HlwmConfig struct { Font string FontBold string Mod string TermSpawnKey string Term string Groups GroupConfig Theme Theme Services ServicesConfig } ServicesConfig struct { Services []string } GroupConfig struct { Groups uint8 Tags uint8 } Theme struct { Colors Colors } Colors struct { Active string Normal string Urgent string Text string } ) func (h HlwmConfig) Save() error { cfg, err := os.Create(cfgPath) if err != nil { return fmt.Errorf("opening cfg path: [%s]: %w", cfgPath, err) } defer cfg.Close() err = toml.NewEncoder(cfg).Encode(h) if err != nil { return fmt.Errorf("encoding config to toml: %w", err) } return cfg.Sync() } func (h HlwmConfig) Validate() error { if h.Font == "" || h.FontBold == "" || h.Mod == "" || h.TermSpawnKey == "" || h.Term == "" || h.Groups.Groups == 0 || h.Groups.Tags == 0 || len(h.Theme.Colors.Active) < 6 || len(h.Theme.Colors.Normal) < 6 || len(h.Theme.Colors.Urgent) < 6 || len(h.Theme.Colors.Text) < 6 { return errors.New("some configuration options are missing") } return nil } // Collect the config from loaded attributes. Or get default // if that fails. func Collect() HlwmConfig { getOrNothing := func(settingName string, value *string) { v, err := Get(settingName) if err != nil { return } *value = v.String() } getUints := func(settingName string, value *uint8) { v, err := Get(settingName) if err != nil { return } val, err := v.Uint() if err != nil { return } *value = uint8(val) } cfg := Default getOrNothing(Font, &cfg.Font) getOrNothing(FontBold, &cfg.FontBold) getOrNothing(ModKey, &cfg.Mod) getOrNothing(TermSpawnKey, &cfg.TermSpawnKey) getOrNothing(Term, &cfg.Term) getUints(GroupCount, &cfg.Groups.Groups) getUints(TagsPerGroup, &cfg.Groups.Tags) getOrNothing(ColorActive, &cfg.Theme.Colors.Active) getOrNothing(ColorNormal, &cfg.Theme.Colors.Normal) getOrNothing(ColorUrgent, &cfg.Theme.Colors.Urgent) getOrNothing(ColorText, &cfg.Theme.Colors.Text) svcsString, err := Get(Services) if err == nil { cfg.Services.Services = strings.Split(svcsString.String(), ";") } return cfg } func (h HlwmConfig) SetAll() error { err := New(GroupCount, strconv.Itoa(int(h.Groups.Groups)), cmd.Uint) if err != nil { return fmt.Errorf("%s: %w", GroupCount, err) } err = New(TagsPerGroup, strconv.Itoa(int(h.Groups.Tags)), cmd.Uint) if err != nil { return fmt.Errorf("%s: %w", TagsPerGroup, err) } sets := map[string]string{ Font: h.Font, FontBold: h.FontBold, ModKey: h.Mod, TermSpawnKey: h.TermSpawnKey, Term: h.Term, ColorActive: h.Theme.Colors.Active, ColorNormal: h.Theme.Colors.Normal, ColorUrgent: h.Theme.Colors.Urgent, ColorText: h.Theme.Colors.Text, Services: strings.Join(h.Services.Services, ";"), } for cfg, val := range sets { if err := New(cfg, val, cmd.String); err != nil { return fmt.Errorf("%s[%s]: %w", cfg, val, err) } } return nil } func Load() HlwmConfig { cfg := HlwmConfig{} if _, err := toml.DecodeFile(cfgPath, &cfg); err != nil { log.Warnf( "could not read file [%s] for config, using default", cfgPath, ) return Default } return cfg } var ( Default = HlwmConfig{ Font: "-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*", FontBold: "-*-fixed-bold-*-*-*-12-*-*-*-*-*-*-*", Mod: "Mod4", TermSpawnKey: "Home", Term: "alacritty", Groups: GroupConfig{ Groups: 3, Tags: 5, }, Theme: Theme{ Colors: Colors{ Active: "#800080", Normal: "#330033", Urgent: "#7811A1", Text: "#898989", }, }, Services: ServicesConfig{ Services: []string{"fcitx5"}, }, } )