package model import ( "errors" "fmt" "os" "strings" "gopkg.in/yaml.v3" ) var ( ErrBirthdaysConfigFileEmpty = errors.New("birthday file cannot be empty") ErrLoggerConfigBadLevel = errors.New("logger level needs to be one of debug, info, warn, error, fatal or left empty") ErrTelegramNotificationsConfigEmptyBotToken = errors.New("bot token cannot be empty") ErrTelegramNotificationsConfigEmptyChannelID = errors.New("channel ID cannot be empty") ) type Config struct { Birthdays *BirthdaysConfig `yaml:"birthdays"` Logger *LoggerConfig `yaml:"logger"` Web *WebConfig `yaml:"web"` TelegramNotifications *TelegramNotificationsConfig `yaml:"telegram_notifications"` } func (c *Config) IsValid() error { if err := c.Birthdays.IsValid(); err != nil { return fmt.Errorf("invalid birthdays config: %w", err) } if err := c.Logger.IsValid(); err != nil { return fmt.Errorf("invalid logger config: %w", err) } if err := c.Web.IsValid(); err != nil { return fmt.Errorf("invalid web config: %w", err) } if c.TelegramNotifications != nil { if err := c.TelegramNotifications.IsValid(); err != nil { return fmt.Errorf("invalid telegram notifications config: %w", err) } } return nil } func (c *Config) SetDefaults() { if c.Birthdays == nil { c.Birthdays = &BirthdaysConfig{} } c.Birthdays.SetDefaults() if c.Logger == nil { c.Logger = &LoggerConfig{} } c.Logger.SetDefaults() if c.Web == nil { c.Web = &WebConfig{} } c.Web.SetDefaults() if c.TelegramNotifications != nil { c.TelegramNotifications.SetDefaults() } } type BirthdaysConfig struct { File string `yaml:"file"` Template string `yaml:"template"` Pictures string `yaml:"pictures"` } func (bc *BirthdaysConfig) IsValid() error { if bc.File == "" { return ErrBirthdaysConfigFileEmpty } return nil } func (bc *BirthdaysConfig) SetDefaults() {} type LoggerConfig struct { Level string `yaml:"level"` ReportCaller bool `yaml:"report_caller"` } func (lc *LoggerConfig) SetDefaults() {} func (lc *LoggerConfig) IsValid() error { level := strings.TrimSpace(strings.ToLower(lc.Level)) found := false for _, ll := range []string{"debug", "info", "warn", "error", "fatal", ""} { if level == ll { found = true break } } if !found { return ErrLoggerConfigBadLevel } return nil } type WebConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` } func (wc *WebConfig) SetDefaults() {} func (wc *WebConfig) IsValid() error { return nil } type TelegramNotificationsConfig struct { BotToken string `yaml:"bot_token"` ChannelID string `yaml:"channel_id"` } func (tnc *TelegramNotificationsConfig) SetDefaults() {} func (tnc *TelegramNotificationsConfig) IsValid() error { if tnc.BotToken == "" { return ErrTelegramNotificationsConfigEmptyBotToken } if tnc.ChannelID == "" { return ErrTelegramNotificationsConfigEmptyChannelID } return nil } func ReadConfig(path string) (*Config, error) { fileBytes, err := os.ReadFile(path) if err != nil { return nil, err } var config *Config if err := yaml.Unmarshal(fileBytes, &config); err != nil { return nil, err } config.SetDefaults() return config, nil }