114 lines
4.8 KiB
Markdown
114 lines
4.8 KiB
Markdown
# config
|
|
|
|
Thread-safe configuration from TOML files, environment variables, command-line
|
|
arguments and registered defaults. Requires Go 1.27.1. The only runtime dependency
|
|
is [lixenwraith/toml](https://github.com/lixenwraith/toml), pinned to merged commit
|
|
`b59d068ff6a4`. Testify and its dependencies are used only by tests.
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
"time"
|
|
"github.com/lixenwraith/config"
|
|
)
|
|
|
|
type Settings struct {
|
|
Port uint16 `toml:"port"`
|
|
Timeout time.Duration `toml:"timeout"`
|
|
}
|
|
|
|
func main() {
|
|
initial := Settings{Port: 8080, Timeout: 5 * time.Second}
|
|
cfg, err := config.NewBuilder().
|
|
WithTarget(&initial).
|
|
WithFile("config.toml").
|
|
WithEnvPrefix("APP_").
|
|
WithArgs([]string{"--port=9090"}).
|
|
Build()
|
|
if err != nil { log.Fatal(err) }
|
|
latest, err := cfg.AsStruct()
|
|
if err != nil { log.Fatal(err) }
|
|
log.Printf("%+v", latest.(*Settings))
|
|
}
|
|
```
|
|
|
|
```toml
|
|
# Service settings
|
|
timeout = "10s"
|
|
port = 8080
|
|
```
|
|
|
|
Precedence is CLI > environment > file > default. Register paths with `Register`
|
|
or structs with `RegisterStruct`; defaults define the types used to validate new
|
|
values. `LoadFile`, `LoadEnv` and `LoadCLI` replace their complete source. A failed
|
|
source load keeps its previous values. Removing an environment variable, CLI flag
|
|
or file key removes the old override on the next load.
|
|
|
|
`Get` returns a detached raw source value: environment and CLI values remain
|
|
strings, TOML integers are int64. `GetTyped[T]`, `Scan`, `ScanSource`, `ScanTyped[T]`
|
|
and `ScanMap` provide checked conversions. Errors leave destinations unchanged;
|
|
missing struct fields retain their existing values. `AsStruct` returns a detached
|
|
snapshot on every call. Build fills `WithTarget` once; subsequent reloads never
|
|
mutate that caller-owned object.
|
|
|
|
## Migration
|
|
|
|
- Only TOML files and `toml` tags are supported. JSON/YAML parsers, format constants
|
|
and alternate tag modes are removed. Existing format/tag option methods accept
|
|
only `toml` (`auto` remains an alias for TOML for file options).
|
|
- Numeric conversion rejects overflow, negative unsigned inputs, fractional
|
|
integer conversion, non-finite floats and integer-to-float precision loss.
|
|
Float64-to-float32 rounding within range remains supported. Full uint64 is
|
|
supported in memory; saving a value above MaxInt64 fails without changing the
|
|
destination file. Use an application-level string representation if needed.
|
|
- Strings can represent decimal numbers, booleans, comma-separated slices/arrays,
|
|
durations, RFC3339 timestamps, IP addresses, CIDR networks and URLs. Boolean to
|
|
string conversion uses `true`/`false`. There are no user decode hooks or automatic
|
|
`encoding.TextUnmarshaler` calls.
|
|
- Setters and source loads reject values incompatible with registered defaults
|
|
immediately. Check their errors. Untyped nil defaults leave the value type open;
|
|
typed nil pointers retain their declared type. Unsupported values, non-finite
|
|
floats and cyclic/deep graphs are rejected. Configuration value depth is 128.
|
|
- All public values, defaults, options and clones own their mutable containers.
|
|
Existing references returned from `AsStruct` are snapshots, not live objects.
|
|
- Saves standardize formatting and retain loaded comments as a preamble. Comment
|
|
positions are not preserved. Existing file permissions are retained; new files
|
|
use 0600. Only registered paths are saved, so unknown file keys are omitted.
|
|
- Paths use dot-separated ASCII key segments. Numeric-only segments and overlapping
|
|
registrations such as `server` plus `server.port` are rejected. Embedded fields
|
|
are not promoted. Recursive struct schemas are unsupported.
|
|
|
|
## Watching
|
|
|
|
```go
|
|
changes := cfg.WatchWithOptions(config.DefaultWatchOptions())
|
|
defer cfg.StopAutoUpdate()
|
|
for event := range changes {
|
|
// Events are hints; always read the latest complete state.
|
|
log.Print(event)
|
|
latest, err := cfg.AsStruct()
|
|
if err != nil { log.Print(err); continue }
|
|
_ = latest.(*Settings)
|
|
}
|
|
```
|
|
|
|
The watcher fingerprints file contents, waits for a quiet debounce interval, then
|
|
validates and publishes. Invalid edits, timeouts, deletion and blocked permission
|
|
changes retain the last valid state. Cancelled, expired or superseded reloads
|
|
cannot publish. Stop closes subscriptions synchronously; a new Watch call can
|
|
restart watching. Notifications are buffered and best effort.
|
|
|
|
## Documentation
|
|
|
|
- [Quick start](doc/quick-start.md), [builder](doc/builder.md), [access](doc/access.md)
|
|
- [Files and saving](doc/file.md), [environment](doc/env.md), [CLI](doc/cli.md)
|
|
- [Live reconfiguration](doc/reconfiguration.md), [validation](doc/validator.md)
|
|
- [Architecture](doc/architecture.md), [audit evidence](doc/audit.md)
|
|
|
|
Run `go test -race ./...` and `go vet ./...`. CI also executes the 386 suite,
|
|
cross-compiles for Windows and runs bounded numeric conversion fuzzing.
|
|
|
|
BSD-3-Clause; see LICENSE.
|