package config import ( "errors" "fmt" "os" "strings" "go.uber.org/multierr" "gopkg.in/yaml.v3" ) var ErrLoggingDisabled error = errors.New("logging is disabled") type Config struct { Environment Environment `yaml:"environment"` Server ServerConfig `yaml:"server"` Database DatabaseConfig `yaml:"database"` RateLimit RateLimitConfig `yaml:"rate_limit"` Observability ObservabilityConfig `yaml:"observability"` } func (c *Config) Validate() error { var err error err = addValidationError(err, "environment validation error", c.Environment.PreprocessAndValidate()) err = addValidationError(err, "server section validation error", c.Server.Validate()) err = addValidationError(err, "database section validation error", c.Database.Validate()) err = addValidationError(err, "rate_limit section validation error", c.RateLimit.Validate()) err = addValidationError(err, "observability section validation error", c.Observability.Validate()) return err } func ReadConfig(path string) (Config, error) { var c Config yamlFile, err := os.ReadFile(path) if err != nil { return c, fmt.Errorf("could not read config: %w", err) } err = yaml.Unmarshal(yamlFile, &c) if err != nil { return c, fmt.Errorf("could not parse yaml: %w", err) } return c, nil } type Environment string var ( ProductionEnvironment Environment = "production" DevelopmentEnvironment Environment = "development" ) func (e *Environment) PreprocessAndValidate() error { preprocessed := strings.ToLower(string(*e)) switch preprocessed { case string(ProductionEnvironment), "prod": *e = ProductionEnvironment return nil case string(DevelopmentEnvironment), "dev": *e = DevelopmentEnvironment return nil default: return fmt.Errorf("invalid Environment value: '%v'", *e) } } func addValidationError(err error, text string, validationError error) error { if validationError == nil { return err } return multierr.Append(err, fmt.Errorf("%s: %w", text, validationError)) }