diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 703fed0..1a2f125 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,8 +15,7 @@ cd rigging make ci # Or run individual checks -make test -make test-race +make test # race-enabled test suite make fmt ``` @@ -41,7 +40,7 @@ make fmt ### Testing Requirements - All exported functions must have tests -- Aim for >80% coverage +- Keep coverage healthy; the current `make ci` gate fails below 70% total coverage - Use table-driven tests - Test error paths, not just happy paths @@ -124,8 +123,8 @@ We reject: **Using Make (recommended):** ```bash -make ci # Run all CI checks (fmt, vet, test, lint) -make test # Run tests +make ci # Run all CI checks (fmt, vet, test, lint, coverage gate) +make test # Run race-enabled tests make fmt # Format code make vet # Run go vet make lint # Run golangci-lint diff --git a/docs/api-reference.md b/docs/api-reference.md index d7aceb9..407973a 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -351,7 +351,7 @@ Configure binding and validation with the `conf` tag: | Tag | Description | Example | |-----|-------------|---------| | `required` | Field must have a value | `conf:"required"` | -| `env:VAR` | Bind from an explicit env-style key path (normalized with `__` -> `.` and lowercased) | `conf:"env:APP__DATABASE__HOST"` | +| `env:VAR` | Bind from an explicit env-style key path (normalized with `__` -> `.` and lowercased) after any `sourceenv.Options.Prefix` stripping | `conf:"env:DATABASE__HOST"` | | `default:X` | Default value if not provided | `conf:"default:8080"` | | `min:N` | Minimum value (numeric) or length (string) | `conf:"min:1024"` | | `max:N` | Maximum value (numeric) or length (string) | `conf:"max:65535"` | @@ -373,7 +373,7 @@ type Config struct { **Tag precedence:** - `name:` overrides all key derivation (ignores `prefix:` and field name) -- `env:` is used when `name:` is not set +- `env:` is used when `name:` is not set; it matches the env-style key after any source prefix stripping - otherwise keys are derived from field names (snake_case), with parent `prefix:` for nested structs - env-style normalization converts `__` to `.` and preserves single `_` diff --git a/docs/configuration-sources.md b/docs/configuration-sources.md index d4b94b0..406eaa1 100644 --- a/docs/configuration-sources.md +++ b/docs/configuration-sources.md @@ -150,11 +150,13 @@ Important: - File source flattens nested objects but does not rewrite separators. - File source can optionally adapt flattened keys via `SnakeCaseKeys` and `KeyPrefix` in `sourcefile.Options`. - Rigging lowercases keys during loader merge for consistent matching across sources. -- If your file keys are snake_case, use `name:` tags to match them. +- Derived field keys are already snake_case, so `MaxConnections` matches `max_connections` without tags. +- Use `name:` only when the source key path differs from the derived path, or use `SnakeCaseKeys` / `KeyPrefix` to adapt file keys at the source boundary. ```go type Config struct { - MaxConnections int `conf:"name:max_connections"` + MaxConnections int // matches max_connections + LegacyPort int `conf:"name:service.port"` } ``` diff --git a/docs/patterns.md b/docs/patterns.md index b3df6c5..3484288 100644 --- a/docs/patterns.md +++ b/docs/patterns.md @@ -39,11 +39,12 @@ type Config struct { } ``` -If your source keys use snake_case or custom paths, map explicitly: +Derived keys already use snake_case. +Use `name:` when a source key path differs from the derived path: ```go type Config struct { - MaxConnections int `conf:"name:max_connections"` + MaxConnections int // matches max_connections APIKey string `conf:"name:api.key"` } ``` @@ -53,11 +54,12 @@ This is useful during migrations or when a legacy env variable name must be pres ```go type Config struct { - DatabaseHost string `conf:"env:APP__DATABASE__HOST,required"` + DatabaseHost string `conf:"env:DATABASE__HOST,required"` } ``` `env:` normalizes env-style syntax (`__` -> `.`, lowercased) before matching. +When `sourceenv` is configured with `Prefix: "APP_"`, `conf:"env:DATABASE__HOST"` matches `APP_DATABASE__HOST`. Prefer `name:` for general cross-source key mapping; use `env:` when the intent is specifically env-key compatibility. ## 4. Normalize Typed Values Before Validation diff --git a/docs/quick-start.md b/docs/quick-start.md index 208ec19..6b9d88b 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -116,15 +116,18 @@ File source behavior: - keys are flattened from file structure and lowercased - separators are not rewritten (for example, `max_connections` stays `max_connections`) -If your file keys are snake_case, map explicitly: +Derived field keys are already snake_case, so `MaxConnections` matches `max_connections` without extra tags. +Use `name:` only when the source key path differs, or adapt file keys at the source boundary with `sourcefile.Options{SnakeCaseKeys: true}` / `KeyPrefix`. ```go type Config struct { - MaxConnections int `conf:"name:max_connections"` + MaxConnections int // matches max_connections + LegacyPort int `conf:"name:service.port"` } ``` -If you need to bind a field to a specific environment-style key path, use `env:` (for example, `conf:"env:APP__DATABASE__HOST"`). +If you need to bind a field to a specific environment-style key path, use `env:`. +The tag matches the normalized key after any `sourceenv.Options.Prefix` stripping, so with `Prefix: "APP_"`, `APP_DATABASE__HOST` maps via `conf:"env:DATABASE__HOST"`. ## 7. Fail-Fast Validation diff --git a/examples/README.md b/examples/README.md index 3b3f089..eb76157 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,7 +5,7 @@ Use this page to quickly find the right example format. ## Runnable Examples - [basic](basic/) - End-to-end app config loading with file + env layering, validation, provenance, and redaction. -- [transformer](transformer/) - Typed `WithTransformer(...)` canonicalization before tag validation. +- [transformer](transformer/) - Typed transform-stage canonicalization before tag validation, shown with `WithTransformerFunc(...)`. Run it from the repository root: diff --git a/examples/basic/README.md b/examples/basic/README.md index b229035..aca7981 100644 --- a/examples/basic/README.md +++ b/examples/basic/README.md @@ -20,11 +20,12 @@ go run main.go # Switch to production environment export APP_ENVIRONMENT=production export APP_DATABASE__HOST=prod-db.example.com +export APP_DATABASE__SSL_MODE=require go run main.go # Enable feature flags export APP_FEATURES__ENABLE_METRICS=true -export APP_FEATURES__RATELIMIT=5000 +export APP_FEATURES__RATE_LIMIT=5000 go run main.go ``` diff --git a/examples/basic/config.yaml b/examples/basic/config.yaml index ed31334..f9cc02c 100644 --- a/examples/basic/config.yaml +++ b/examples/basic/config.yaml @@ -9,16 +9,16 @@ database: name: myapp_staging user: app_user password: staging_password_123 - maxconnections: 25 - sslmode: require - connecttimeout: 10s + max_connections: 25 + ssl_mode: require + connect_timeout: 10s server: host: 0.0.0.0 port: 8080 - readtimeout: 30s - writetimeout: 30s - shutdowntimeout: 10s + read_timeout: 30s + write_timeout: 30s + shutdown_timeout: 10s logging: level: info @@ -26,6 +26,6 @@ logging: output: stdout features: - enablemetrics: true - enabletracing: false - ratelimit: 1000 + enable_metrics: true + enable_tracing: false + rate_limit: 1000