package config import ( "fmt" "os" "strconv" "github.com/rs/zerolog" ) // Environment variable names const ( EnvLogLevel = "LOG_LEVEL" EnvRootPath = "ROOT_PATH" EnvNumWorkers = "NUM_OF_WORKERS" EnvWorkerBatchSize = "WORKER_BATCH_SIZE" EnvMaxResponseSize = "MAX_RESPONSE_SIZE" EnvResponseTimeout = "RESPONSE_TIMEOUT" ) // Default configuration values const ( DefaultLogLevel = zerolog.InfoLevel DefaultNumWorkers = 5 DefaultWorkerBatchSize = 100 DefaultMaxResponseSize = 1048576 // 1MB DefaultResponseTimeout = 30 // seconds ) // Config holds the application configuration loaded from environment variables type Config struct { LogLevel zerolog.Level // Logging level (debug, info, warn, error) RootPath string // Root path for the application MaxResponseSize int // Maximum size of response in bytes NumOfWorkers int // Number of concurrent workers ResponseTimeout int // Timeout for responses in seconds WorkerBatchSize int // Batch size for worker processing } // String returns a string representation of the configuration func (c *Config) String() string { return fmt.Sprintf( "Config{LogLevel: %s, RootPath: %s, MaxResponseSize: %d, NumWorkers: %d, ResponseTimeout: %d, WorkerBatchSize: %d}", c.LogLevel, c.RootPath, c.MaxResponseSize, c.NumOfWorkers, c.ResponseTimeout, c.WorkerBatchSize, ) } var CONFIG Config // parsePositiveInt parses and validates positive integer values func parsePositiveInt(value string) (int, error) { val, err := strconv.Atoi(value) if err != nil { return 0, err } if val <= 0 { return 0, fmt.Errorf("value must be positive") } return val, nil } // GetConfig loads and validates configuration from environment variables func GetConfig() *Config { config := &Config{} // Map of environment variables to their parsing functions parsers := map[string]func(string) error{ EnvLogLevel: func(v string) error { level, err := zerolog.ParseLevel(v) if err != nil { return fmt.Errorf("invalid log level: %w", err) } config.LogLevel = level return nil }, EnvRootPath: func(v string) error { if _, err := os.Stat(v); err != nil { return fmt.Errorf("invalid root path: %w", err) } config.RootPath = v return nil }, EnvNumWorkers: func(v string) error { val, err := parsePositiveInt(v) if err != nil { return fmt.Errorf("invalid number of workers: %w", err) } config.NumOfWorkers = val return nil }, EnvWorkerBatchSize: func(v string) error { val, err := parsePositiveInt(v) if err != nil { return fmt.Errorf("invalid worker batch size: %w", err) } config.WorkerBatchSize = val return nil }, EnvMaxResponseSize: func(v string) error { val, err := parsePositiveInt(v) if err != nil { return fmt.Errorf("invalid max response size: %w", err) } config.MaxResponseSize = val return nil }, EnvResponseTimeout: func(v string) error { val, err := parsePositiveInt(v) if err != nil { return fmt.Errorf("invalid response timeout: %w", err) } config.ResponseTimeout = val return nil }, } // Process each environment variable for envVar, parser := range parsers { value, ok := os.LookupEnv(envVar) if !ok { fmt.Fprintf(os.Stderr, "Missing required environment variable: %s\n", envVar) os.Exit(1) } if err := parser(value); err != nil { fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err) os.Exit(1) } } return config }