package main import ( "fmt" "gopkg.in/yaml.v3" "os" ) type Config struct { Links []Link `yaml:"links"` Monitor []Item `yaml:"monitor"` Exec []Item `yaml:"exec"` } func loadConfig(path string) (*Config, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() var cfg Config dec := yaml.NewDecoder(f) dec.KnownFields(true) if err := dec.Decode(&cfg); err != nil { return nil, fmt.Errorf("parse %s: %w", path, err) } seen := map[string]bool{} // item names are global keys (monVals, RunScript) for _, l := range cfg.Links { if seen[l.Name] { return nil, fmt.Errorf("duplicate name %q", l.Name) } seen[l.Name] = true } for _, list := range [][]Item{cfg.Monitor, cfg.Exec} { for _, it := range list { if seen[it.Name] { return nil, fmt.Errorf("duplicate name %q", it.Name) } seen[it.Name] = true } } for _, it := range cfg.Monitor { if it.Refresh <= 0 { return nil, fmt.Errorf("monitor %q: refresh must be > 0", it.Name) } if it.Data != "text" && it.Data != "percentage" { return nil, fmt.Errorf("monitor %q: data must be text|percentage", it.Name) } } for _, it := range cfg.Exec { if len(it.ArgTypes) != it.Args { return nil, fmt.Errorf("exec %q: arg_types must have args entries", it.Name) } for _, t := range it.ArgTypes { if t != "text" && t != "number" { return nil, fmt.Errorf("exec %q: arg type must be text|number", it.Name) } } } return &cfg, nil }