package config import ( "bufio" "fmt" "os" "strconv" "strings" ) // LoadDotEnv reads KEY=VALUE pairs from path (default ".env") and applies // them via os.Setenv, without overriding variables already set in the // environment. Missing file is not an error — real env vars always win. func LoadDotEnv(path string) error { if path == "" { path = ".env" } f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return nil } return fmt.Errorf("open %s: %w", path, err) } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } key, value, ok := strings.Cut(line, "=") if !ok { continue } key = strings.TrimSpace(key) value = strings.Trim(strings.TrimSpace(value), `"'`) if _, exists := os.LookupEnv(key); !exists { os.Setenv(key, value) } } return scanner.Err() } func GetString(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func GetInt(key string, fallback int) int { v := os.Getenv(key) if v == "" { return fallback } n, err := strconv.Atoi(v) if err != nil { return fallback } return n } func MustGetString(key string) (string, error) { v := os.Getenv(key) if v == "" { return "", fmt.Errorf("missing required environment variable: %s", key) } return v, nil }