diff --git a/.gitignore b/.gitignore index 9c6d0be..0377e3d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/*~ /.idea /run.sh +/dist diff --git a/config/config.go b/config/config.go index 32160dc..5240159 100644 --- a/config/config.go +++ b/config/config.go @@ -1,126 +1,69 @@ package config import ( + "flag" "fmt" + "log/slog" "os" - "strconv" - - "github.com/rs/zerolog" + "strings" ) -// Environment variable names. -const ( - EnvLogLevel = "LOG_LEVEL" - EnvResponseTimeout = "RESPONSE_TIMEOUT" - EnvPanicOnUnexpectedError = "PANIC_ON_UNEXPECTED_ERROR" - EnvRootPath = "ROOT_PATH" - EnvDirIndexingEnabled = "DIR_INDEXING_ENABLED" -) - -// Config holds the application configuration loaded from environment variables. +// Config holds the application configuration loaded from CLI flags. type Config struct { - LogLevel zerolog.Level // Logging level (debug, info, warn, error) - ResponseTimeout int // Timeout for responses in seconds - PanicOnUnexpectedError bool // Panic on unexpected errors when visiting a URL - RootPath string // Path to serve files from - DirIndexingEnabled bool // Allow client to browse directories or not + LogLevel slog.Level // Logging level (debug, info, warn, error) + ResponseTimeout int // Timeout for responses in seconds + RootPath string // Path to serve files from + DirIndexingEnabled bool // Allow client to browse directories or not + Listen string // Address to listen on } var CONFIG Config //nolint:gochecknoglobals -// parsePositiveInt parses and validates positive integer values. -func parsePositiveInt(param, value string) (int, error) { - val, err := strconv.Atoi(value) - if err != nil { - return 0, ValidationError{ - Param: param, - Value: value, - Reason: "must be a valid integer", - } +// parseLogLevel parses a log level string into slog.Level +func parseLogLevel(level string) (slog.Level, error) { + switch strings.ToLower(level) { + case "debug": + return slog.LevelDebug, nil + case "info": + return slog.LevelInfo, nil + case "warn", "warning": + return slog.LevelWarn, nil + case "error": + return slog.LevelError, nil + default: + return slog.LevelInfo, fmt.Errorf("invalid log level: %s", level) } - if val <= 0 { - return 0, ValidationError{ - Param: param, - Value: value, - Reason: "must be positive", - } - } - return val, nil } -func parseBool(param, value string) (bool, error) { - val, err := strconv.ParseBool(value) - if err != nil { - return false, ValidationError{ - Param: param, - Value: value, - Reason: "cannot be converted to boolean", - } - } - return val, nil -} - -// GetConfig loads and validates configuration from environment variables +// GetConfig loads and validates configuration from CLI flags func GetConfig() *Config { - config := &Config{} + // Define CLI flags with defaults + logLevel := flag.String("log-level", "info", "Logging level (debug, info, warn, error)") + responseTimeout := flag.Int("response-timeout", 30, "Timeout for responses in seconds") + rootPath := flag.String("root-path", "", "Path to serve files from") + dirIndexing := flag.Bool("dir-indexing", false, "Allow client to browse directories") + listen := flag.String("listen", "localhost:1965", "Address to listen on") - // 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 ValidationError{ - Param: EnvLogLevel, - Value: v, - Reason: "must be one of: debug, info, warn, error", - } - } - config.LogLevel = level - return nil - }, - EnvResponseTimeout: func(v string) error { - val, err := parsePositiveInt(EnvResponseTimeout, v) - if err != nil { - return err - } - config.ResponseTimeout = val - return nil - }, - EnvPanicOnUnexpectedError: func(v string) error { - val, err := parseBool(EnvPanicOnUnexpectedError, v) - if err != nil { - return err - } - config.PanicOnUnexpectedError = val - return nil - }, - EnvRootPath: func(v string) error { - config.RootPath = v - return nil - }, - EnvDirIndexingEnabled: func(v string) error { - val, err := parseBool(EnvDirIndexingEnabled, v) - if err != nil { - return err - } - config.DirIndexingEnabled = val - return nil - }, + flag.Parse() + + // Parse and validate log level + level, err := parseLogLevel(*logLevel) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Invalid log level '%s': must be one of: debug, info, warn, error\n", *logLevel) + os.Exit(1) } - // 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) - } + // Validate response timeout + if *responseTimeout <= 0 { + _, _ = fmt.Fprintf(os.Stderr, "Invalid response timeout '%d': must be positive\n", *responseTimeout) + os.Exit(1) } - return config + return &Config{ + LogLevel: level, + ResponseTimeout: *responseTimeout, + RootPath: *rootPath, + DirIndexingEnabled: *dirIndexing, + Listen: *listen, + } } diff --git a/config/errors.go b/config/errors.go deleted file mode 100644 index 60482d7..0000000 --- a/config/errors.go +++ /dev/null @@ -1,14 +0,0 @@ -package config - -import "fmt" - -// ValidationError represents a config validation error -type ValidationError struct { - Param string - Value string - Reason string -} - -func (e ValidationError) Error() string { - return fmt.Sprintf("invalid value '%s' for %s: %s", e.Value, e.Param, e.Reason) -}