package config import ( "fmt" "net/url" "strings" "gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil" ) func Validate(cfg Config) error { if cfg.WeatherAPI.BaseURL != "" { parsed, err := url.Parse(cfg.WeatherAPI.BaseURL) if err != nil || parsed.Scheme == "" || parsed.Host == "" { return fmt.Errorf("weather_api.base_url must be an absolute URL") } } if cfg.WeatherAPI.Timeout <= 0 { return fmt.Errorf("weather_api.timeout must be greater than zero") } if cfg.WeatherAPI.Precision < 0 { return fmt.Errorf("weather_api.precision must be zero or greater") } if cfg.WeatherAPI.Units == "" { return fmt.Errorf("weather_api.units is required") } if cfg.WeatherAPI.Timezone == "" { return fmt.Errorf("weather_api.timezone is required") } if _, err := timeutil.LoadLocation(cfg.WeatherAPI.Timezone); err != nil { return fmt.Errorf("weather_api.timezone %q is invalid: %w", cfg.WeatherAPI.Timezone, err) } if cfg.WeatherAPI.Format == "" { return fmt.Errorf("weather_api.format is required") } if cfg.WeatherAPI.Format != "json" { return fmt.Errorf("weather_api.format must be json") } if err := validatePolicy("missing_source.default", cfg.MissingSource.Default); err != nil { return err } for source, policy := range cfg.MissingSource.Sources { if strings.TrimSpace(source) == "" { return fmt.Errorf("missing_source.sources contains an empty source name") } if err := validatePolicy("missing_source.sources."+source, policy); err != nil { return err } } if cfg.Scriptorium.Binary == "" { return fmt.Errorf("scriptorium.binary is required") } if cfg.Scriptorium.Timeout <= 0 { return fmt.Errorf("scriptorium.timeout must be greater than zero") } if cfg.Workspace.Root == "" { return fmt.Errorf("workspace.root is required") } if cfg.Reports.OutputDir == "" { return fmt.Errorf("reports.output_dir is required") } if len(cfg.Dayparts) == 0 { return fmt.Errorf("dayparts must contain at least one entry") } for i, daypart := range cfg.Dayparts { if strings.TrimSpace(daypart.Name) == "" { return fmt.Errorf("dayparts[%d].name is required", i) } if _, err := timeutil.ParseClock(daypart.Start); err != nil { return fmt.Errorf("dayparts[%d].start is invalid: %w", i, err) } if _, err := timeutil.ParseClock(daypart.End); err != nil { return fmt.Errorf("dayparts[%d].end is invalid: %w", i, err) } } return nil } func validatePolicy(name string, policy MissingSourcePolicy) error { switch policy { case MissingSourceError, MissingSourceWarn, MissingSourceNone: return nil default: return fmt.Errorf("%s must be one of error, warn, or none", name) } }