ebf246beb0
- dotenv_load: parser .env con no-sobreescritura y soporte de comillas - env_require: os.Getenv con error descriptivo fail-fast - env_require_all: verifica multiples vars y lista todas las faltantes - config_from_env: reflection sobre struct tags env/default/required/secret, 5 tipos soportados - config_from_file: JSON via stdlib, YAML stub con not-implemented - 25 tests unitarios, todos PASS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
74 lines
1.8 KiB
Go
74 lines
1.8 KiB
Go
package infra
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// DotenvLoad parses a .env file and sets environment variables via os.Setenv.
|
|
// It does NOT overwrite variables that are already set in the environment.
|
|
//
|
|
// Parsing rules:
|
|
// - Lines starting with # (after trimming) are ignored as comments.
|
|
// - Blank lines are ignored.
|
|
// - Lines must follow the KEY=VALUE format.
|
|
// - Values may be optionally quoted with single or double quotes; quotes are stripped.
|
|
// - Inline comments (# after value) are NOT stripped — the raw value after = is used.
|
|
//
|
|
// Returns an error if the file cannot be opened or if a line has no '=' separator.
|
|
func DotenvLoad(path string) error {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return fmt.Errorf("dotenv_load: open %q: %w", path, err)
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
lineNum := 0
|
|
for scanner.Scan() {
|
|
lineNum++
|
|
line := strings.TrimSpace(scanner.Text())
|
|
|
|
// skip blank lines and comments
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
|
|
idx := strings.IndexByte(line, '=')
|
|
if idx < 0 {
|
|
return fmt.Errorf("dotenv_load: %q line %d: missing '=' in %q", path, lineNum, line)
|
|
}
|
|
|
|
key := strings.TrimSpace(line[:idx])
|
|
val := line[idx+1:]
|
|
|
|
// strip surrounding quotes from value
|
|
val = stripQuotes(val)
|
|
|
|
// only set if not already present
|
|
if os.Getenv(key) == "" {
|
|
if err := os.Setenv(key, val); err != nil {
|
|
return fmt.Errorf("dotenv_load: setenv %q: %w", key, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return fmt.Errorf("dotenv_load: scan %q: %w", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// stripQuotes removes surrounding single or double quotes from a string.
|
|
func stripQuotes(s string) string {
|
|
if len(s) >= 2 {
|
|
if (s[0] == '"' && s[len(s)-1] == '"') ||
|
|
(s[0] == '\'' && s[len(s)-1] == '\'') {
|
|
return s[1 : len(s)-1]
|
|
}
|
|
}
|
|
return s
|
|
}
|