// Package config loads he-dydns configuration from a TOML file. package config import ( "fmt" "os" "path/filepath" "github.com/BurntSushi/toml" ) const DefaultIntervalMinutes = 5 // Entry describes a single Hurricane Electric dynamic DNS hostname to keep updated. type Entry struct { Hostname string `toml:"hostname"` Token string `toml:"token"` } // Config is the top-level he-dydns configuration. type Config struct { // Interface, if set, restricts address discovery to this network // interface instead of guessing the public IP via an external service. Interface string `toml:"interface"` // IntervalMinutes controls how often updates run in daemon mode. IntervalMinutes int `toml:"interval_minutes"` Entries []Entry `toml:"entries"` } // DefaultPath returns $HOME/.config/he-dydns/config.toml. func DefaultPath() (string, error) { home, err := os.UserHomeDir() if err != nil { return "", fmt.Errorf("determining home directory: %w", err) } return filepath.Join(home, ".config", "he-dydns", "config.toml"), nil } // Load reads and validates the configuration file at path. func Load(path string) (*Config, error) { var cfg Config if _, err := toml.DecodeFile(path, &cfg); err != nil { return nil, fmt.Errorf("reading config %s: %w", path, err) } if len(cfg.Entries) == 0 { return nil, fmt.Errorf("config %s: no entries defined", path) } for i, e := range cfg.Entries { if e.Hostname == "" { return nil, fmt.Errorf("config %s: entries[%d] missing hostname", path, i) } if e.Token == "" { return nil, fmt.Errorf("config %s: entries[%d] missing token", path, i) } } if cfg.IntervalMinutes <= 0 { cfg.IntervalMinutes = DefaultIntervalMinutes } return &cfg, nil }