Files
config/README.md
T

116 lines
5.0 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).
```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, durations, RFC3339
timestamps, IP addresses, CIDR networks and URLs. For a slice or array path,
environment and command-line text splits at commas; a string from a file,
`Set` or `ScanMap` is one element, since those have native arrays and a pattern
may hold a comma. Boolean to string conversion uses `true`/`false`.
Conversion errors name the path, never the value. 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 a permission
change, reported once before the next edit applies, 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.