lixen 2a47be454d
Test / test (push) Canceled after 0s
v0.2.1 remove testify dep
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2025-11-08 07:16:48 -05:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2025-11-08 07:16:48 -05:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00
2026-09-29 01:11:44 -04:00

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.

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))
}
# 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

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

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.

S
Description
No description provided
Readme BSD-3-Clause
604 KiB
0 Stars 1 Watchers 0 Forks
Languages
Go 100%