Documentation
¶
Overview ¶
Package config loads deterministic layered configuration snapshots.
Example (DefaultsAndProgrammaticSources) ¶
package main
import (
"context"
"fmt"
config "github.com/faustbrian/go-config"
"github.com/faustbrian/go-config/defaults"
"github.com/faustbrian/go-config/programmatic"
)
func main() {
type settings struct {
Name string `config:"name" default:"default"`
Region string `config:"region"`
Port int `config:"port"`
}
typedDefaults, _ := defaults.For[settings]("typed-defaults")
mapDefaults, _ := programmatic.Defaults(
"map-defaults",
map[string]any{"region": "eu"},
)
middle, _ := programmatic.Map(
"runtime",
config.PriorityExplicitFiles,
map[string]any{"port": int64(8080)},
)
overrides, _ := programmatic.Overrides(
"overrides",
map[string]any{"name": "worker"},
)
plan, _ := config.NewPlan(overrides, middle, mapDefaults, typedDefaults)
snapshot, _ := config.Load[settings](context.Background(), plan)
value := snapshot.Value()
fmt.Println(value.Name, value.Region, value.Port)
}
Output: worker eu 8080
Example (DiscoveryAndValidation) ¶
package main
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
config "github.com/faustbrian/go-config"
"github.com/faustbrian/go-config/discover"
"github.com/faustbrian/go-config/filesystem"
"github.com/faustbrian/go-config/validation"
)
func main() {
directory, _ := os.MkdirTemp("", "config-discovery-")
defer func() { _ = os.RemoveAll(directory) }()
path := filepath.Join(directory, "app.yaml")
_ = os.WriteFile(path, []byte("port: 8080\n"), 0o600)
permissions := discover.OwnerOnly
if runtime.GOOS == "windows" {
permissions = discover.IgnorePermissions
}
results, _ := discover.Search(context.Background(), discover.Options{
Root: directory, Directories: []string{directory},
SearchPlaces: []string{"app.yaml"}, Mode: discover.SearchFirst,
Symlinks: discover.RejectSymlinks, Permissions: permissions,
})
source, _ := filesystem.FromDiscovered(
results[0],
filesystem.Options{Name: "discovered", Priority: config.PriorityDiscoveredBase},
)
type settings struct {
Port int `config:"port,required"`
}
plan, _ := config.NewPlan(source)
snapshot, _ := config.LoadWithValidators(
context.Background(),
plan,
func(_ context.Context, value settings) error {
if value.Port < 1 || value.Port > 65535 {
return validation.At("port", errors.New("port outside supported range"))
}
return nil
},
)
fmt.Println(snapshot.Value().Port, results[0].SearchPlace)
}
Output: 8080 app.yaml
Example (DotenvSources) ¶
package main
import (
"context"
"fmt"
"testing/fstest"
config "github.com/faustbrian/go-config"
"github.com/faustbrian/go-config/dotenv"
)
func main() {
type settings struct {
FromBytes string `config:"from_bytes" env:"FROM_BYTES"`
FromFS string `config:"from_fs" env:"FROM_FS"`
}
files := fstest.MapFS{
"settings.env": &fstest.MapFile{Data: []byte("APP_FROM_FS=${VALUE:-filesystem}\n")},
}
fromBytes, _ := dotenv.BytesFor[settings](
[]byte("APP_FROM_BYTES=${VALUE:-bytes}\n"),
dotenv.Options{
Name: "dotenv-bytes", Prefix: "APP_",
Interpolation: &dotenv.Interpolation{IncludeFile: true},
},
)
fromFS, _ := dotenv.FromFSFor[settings](
files,
"settings.env",
dotenv.Options{
Name: "dotenv-fs", Prefix: "APP_",
Interpolation: &dotenv.Interpolation{IncludeFile: true},
},
)
plan, _ := config.NewPlan(fromBytes, fromFS)
snapshot, _ := config.Load[settings](context.Background(), plan)
fmt.Println(snapshot.Value().FromBytes, snapshot.Value().FromFS)
}
Output: bytes filesystem
Example (EnvironmentSources) ¶
package main
import (
"context"
"fmt"
"os"
config "github.com/faustbrian/go-config"
"github.com/faustbrian/go-config/environment"
)
func main() {
type settings struct {
Explicit string `config:"explicit" env:"EXPLICIT"`
Process string `config:"process" env:"PROCESS"`
}
const processName = "GO_CONFIG_EXAMPLE_PROCESS"
previous, existed := os.LookupEnv(processName)
_ = os.Setenv(processName, "process")
defer func() {
if existed {
_ = os.Setenv(processName, previous)
} else {
_ = os.Unsetenv(processName)
}
}()
explicit, _ := environment.EnvironFor[settings](
[]string{"GO_CONFIG_EXAMPLE_EXPLICIT=explicit"},
environment.Options{Name: "explicit", Prefix: "GO_CONFIG_EXAMPLE_"},
)
process, _ := environment.ProcessFor[settings](environment.Options{
Name: "process", Prefix: "GO_CONFIG_EXAMPLE_",
})
plan, _ := config.NewPlan(explicit, process)
snapshot, _ := config.Load[settings](context.Background(), plan)
fmt.Println(snapshot.Value().Explicit, snapshot.Value().Process)
}
Output: explicit process
Example (FilesystemSources) ¶
package main
import (
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
"testing/fstest"
config "github.com/faustbrian/go-config"
"github.com/faustbrian/go-config/discover"
"github.com/faustbrian/go-config/filesystem"
)
func main() {
files := fstest.MapFS{
"embedded.json": &fstest.MapFile{Data: []byte(`{"embedded":"loaded"}`)},
}
fromFS, _ := filesystem.FromFS(
files,
"embedded.json",
filesystem.Options{Name: "embedded"},
)
reader, _ := filesystem.Reader(
func(context.Context) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewBufferString("reader: loaded\n")), nil
},
filesystem.Options{Name: "reader", Format: filesystem.FormatYAML},
)
directory, _ := os.MkdirTemp("", "config-example-")
defer func() { _ = os.RemoveAll(directory) }()
path := filepath.Join(directory, "settings.toml")
_ = os.WriteFile(path, []byte(`path = "loaded"`), 0o600)
fromPath, _ := filesystem.FromPath(path, filesystem.Options{Name: "path"})
fromDiscovery, _ := filesystem.FromDiscovered(
discover.Result{Path: path, ResolvedPath: path},
filesystem.Options{Name: "discovered"},
)
plan, _ := config.NewPlan(fromFS, reader, fromPath, fromDiscovery)
snapshot, _ := config.LoadTree(context.Background(), plan)
value := snapshot.Value()
fmt.Println(value["embedded"], value["reader"], value["path"])
}
Output: loaded loaded loaded
Example (SecretsProvenanceAndConfigtest) ¶
package main
import (
"context"
"errors"
"fmt"
config "github.com/faustbrian/go-config"
"github.com/faustbrian/go-config/configtest"
)
func main() {
type settings struct {
Token config.Secret `config:"token,secret"`
}
fixture := configtest.NewSource(
config.SourceInfo{Name: "fixture", Sensitive: true},
config.Document{Tree: map[string]any{"token": "secret-value"}},
)
plan, _ := config.NewPlan(fixture)
snapshot, _ := config.Load[settings](context.Background(), plan)
origin, _ := snapshot.Origin("token")
environmentFixture := configtest.Environment(map[string]string{"B": "2", "A": "1"})
filesystemFixture := configtest.Filesystem(map[string]string{"config.json": `{}`})
_, openErr := filesystemFixture.Open("config.json")
failure := errors.New("fixture failure")
failingPlan, _ := config.NewPlan(configtest.FailingSource(
config.SourceInfo{Name: "failure"},
failure,
))
_, loadErr := config.LoadTree(context.Background(), failingPlan)
fmt.Println(snapshot.Value().Token, origin.Sensitive)
fmt.Println(environmentFixture, openErr == nil, errors.Is(loadErr, failure))
}
Output: [REDACTED] true [A=1 B=2] true true
Example (StructuredSources) ¶
package main
import (
"context"
"fmt"
"testing/fstest"
config "github.com/faustbrian/go-config"
jsonsource "github.com/faustbrian/go-config/json"
tomlsource "github.com/faustbrian/go-config/toml"
yamlsource "github.com/faustbrian/go-config/yaml"
)
func main() {
files := fstest.MapFS{
"settings.json": &fstest.MapFile{Data: []byte(`{"json_fs":"loaded"}`)},
"settings.yaml": &fstest.MapFile{Data: []byte("yaml_fs: loaded\n")},
"settings.toml": &fstest.MapFile{Data: []byte(`toml_fs = "loaded"`)},
}
jsonBytes, _ := jsonsource.Bytes(
[]byte(`{"json_bytes":"loaded"}`),
jsonsource.Options{Name: "json-bytes"},
)
jsonFS, _ := jsonsource.FromFS(
files,
"settings.json",
jsonsource.Options{Name: "json-fs"},
)
yamlBytes, _ := yamlsource.Bytes(
[]byte("yaml_bytes: loaded\n"),
yamlsource.Options{Name: "yaml-bytes"},
)
yamlFS, _ := yamlsource.FromFS(
files,
"settings.yaml",
yamlsource.Options{Name: "yaml-fs"},
)
tomlBytes, _ := tomlsource.Bytes(
[]byte(`toml_bytes = "loaded"`),
tomlsource.Options{Name: "toml-bytes"},
)
tomlFS, _ := tomlsource.FromFS(
files,
"settings.toml",
tomlsource.Options{Name: "toml-fs"},
)
plan, _ := config.NewPlan(jsonBytes, jsonFS, yamlBytes, yamlFS, tomlBytes, tomlFS)
snapshot, _ := config.LoadTree(context.Background(), plan)
value := snapshot.Value()
fmt.Println(
value["json_bytes"], value["json_fs"],
value["yaml_bytes"], value["yaml_fs"],
value["toml_bytes"], value["toml_fs"],
)
}
Output: loaded loaded loaded loaded loaded loaded
Index ¶
- Constants
- Variables
- type ByteSize
- type ContextCloser
- type ContextFS
- type ContextFile
- type DefaultSources
- type Document
- type GenerationFile
- type Optional
- type Origin
- type Plan
- type Presence
- type Secret
- type Snapshot
- type SnapshotValueError
- type Source
- type SourceError
- type SourceInfo
- type TreeCycleError
- type TreeLimitError
- type TreeValueError
Examples ¶
Constants ¶
const ( PriorityDefaults = 10 PriorityDiscoveredBase = 20 PriorityDiscoveredProfile = 30 PriorityExplicitFiles = 40 PriorityDotenv = 50 PriorityEnvironment = 60 PriorityOverrides = 70 )
Documented default source priorities, ordered from lowest to highest.
const Redacted = "[REDACTED]"
Redacted is the stable replacement used for secret values.
Variables ¶
var ErrNotFound = errors.New("configuration source not found")
ErrNotFound is returned by sources that are absent. Only optional sources suppress this error.
var ErrSourceChanged = errors.New("configuration source changed during read")
ErrSourceChanged indicates that a filesystem source changed while it was being read, so no candidate snapshot was accepted.
Functions ¶
This section is empty.
Types ¶
type ByteSize ¶
type ByteSize int64
ByteSize is a count of bytes decoded from values such as 10MiB or 2GB.
func (*ByteSize) UnmarshalText ¶
UnmarshalText parses an integer followed by an optional supported unit.
type ContextCloser ¶
ContextCloser exposes a cancellable close operation for remote resources. Filesystem sources call it with an independent bounded cleanup context and otherwise call Close.
type ContextFS ¶
ContextFS extends fs.FS with a cancellable open operation. Filesystem source constructors use it when implemented and otherwise call fs.FS.Open.
type ContextFile ¶
type ContextFile interface {
fs.File
ReadContext(context.Context, []byte) (int, error)
StatContext(context.Context) (fs.FileInfo, error)
}
ContextFile extends fs.File with cancellable read and metadata operations. Filesystem sources use these methods when implemented.
type DefaultSources ¶
type DefaultSources struct {
Defaults []Source
DiscoveredBase []Source
DiscoveredProfile []Source
ExplicitFiles []Source
Dotenv []Source
Environment []Source
Overrides []Source
}
DefaultSources groups sources by the documented default precedence model. Order within each category is preserved.
type GenerationFile ¶
GenerationFile exposes an opaque stable generation token. Filesystem sources compare it before and after reads to reject mixed generations.
type Optional ¶
type Optional[T any] struct { // contains filtered or unexported fields }
Optional preserves absence, explicit null, and present zero values.
func (Optional[T]) ConfigTextTarget ¶
ConfigTextTarget identifies T to typed textual sources.
func (*Optional[T]) UnmarshalConfigValue ¶
UnmarshalConfigValue implements decode.ValueUnmarshaler.
type Origin ¶
type Origin struct {
Source string
Location string
Sensitive bool
Deprecated bool
Present bool
State Presence
}
Origin identifies the winning source for a configuration path.
type Plan ¶
type Plan struct {
// contains filtered or unexported fields
}
Plan is an immutable, inspectable low-to-high precedence source plan.
func NewDefaultPlan ¶
func NewDefaultPlan(sources DefaultSources) (Plan, error)
NewDefaultPlan assigns category priorities and returns an inspectable plan.
func NewPlan ¶
NewPlan validates sources and orders them from lowest to highest priority. Sources with equal priority retain caller order.
func (Plan) Sources ¶
func (p Plan) Sources() []SourceInfo
Sources returns safe metadata in resolved precedence order.
type Presence ¶
type Presence uint8
Presence distinguishes values that ordinary Go zero values cannot.
type Secret ¶
type Secret struct {
// contains filtered or unexported fields
}
Secret stores sensitive text with redacted default formatting and marshaling. Reveal is the only supported way to obtain the underlying value.
func (Secret) MarshalJSON ¶
MarshalJSON prevents JSON diagnostics from exposing the value.
func (Secret) MarshalText ¶
MarshalText prevents text-based encoders from exposing the value.
func (*Secret) UnmarshalText ¶
UnmarshalText allows strict decoders to populate Secret from string values.
type Snapshot ¶
type Snapshot[T any] struct { // contains filtered or unexported fields }
Snapshot is an immutable configuration tree and its safe provenance.
func LoadTree ¶
LoadTree resolves a Plan atomically. Any source or merge failure returns no snapshot.
func LoadWithValidators ¶
func LoadWithValidators[T any]( ctx context.Context, plan Plan, validators ...validation.Validator[T], ) (*Snapshot[T], error)
LoadWithValidators resolves, decodes, and validates a Plan atomically.
type SnapshotValueError ¶
SnapshotValueError reports typed state that cannot be copied without retaining caller-visible mutable references. It never formats the value.
func (*SnapshotValueError) Error ¶
func (e *SnapshotValueError) Error() string
type Source ¶
type Source interface {
Info() SourceInfo
Load(context.Context) (Document, error)
}
Source loads one configuration document without mutating global state.
type SourceError ¶
SourceError identifies a failed source without exposing arbitrary source error text. Cause identity remains available through errors.Is.
func (*SourceError) Error ¶
func (e *SourceError) Error() string
func (*SourceError) MarshalText ¶
func (e *SourceError) MarshalText() ([]byte, error)
MarshalText serializes only the redacted diagnostic message.
func (*SourceError) Unwrap ¶
func (e *SourceError) Unwrap() error
type SourceInfo ¶
SourceInfo describes a source without exposing its values.
type TreeCycleError ¶
type TreeCycleError struct {
Path string
}
TreeCycleError reports a cyclic source tree without traversing it.
func (*TreeCycleError) Error ¶
func (e *TreeCycleError) Error() string
type TreeLimitError ¶
TreeLimitError reports a source-tree structural bound without formatting a value.
func (*TreeLimitError) Error ¶
func (e *TreeLimitError) Error() string
type TreeValueError ¶
TreeValueError reports a non-canonical value returned by a Source. It never formats the rejected value.
func (*TreeValueError) Error ¶
func (e *TreeValueError) Error() string
Directories
¶
| Path | Synopsis |
|---|---|
|
adapters
|
|
|
service
Package configservice adapts typed configuration plans to service command loaders without owning long-lived resources.
|
Package configservice adapts typed configuration plans to service command loaders without owning long-lived resources. |
|
awssecretsmanager
module
|
|
|
Package configservice is the compatibility import path for the target-oriented service adapter at github.com/faustbrian/go-config/adapters/service.
|
Package configservice is the compatibility import path for the target-oriented service adapter at github.com/faustbrian/go-config/adapters/service. |
|
Package configtest provides deterministic configuration fixtures and test assertions without mutating process-global state.
|
Package configtest provides deterministic configuration fixtures and test assertions without mutating process-global state. |
|
Package decode maps format-independent configuration trees into Go values.
|
Package decode maps format-independent configuration trees into Go values. |
|
Package defaults builds typed lowest-precedence sources from struct tags.
|
Package defaults builds typed lowest-precedence sources from struct tags. |
|
Package discover provides explicit, bounded configuration file discovery.
|
Package discover provides explicit, bounded configuration file discovery. |
|
Package dotenv provides strict, bounded, optionally interpolated dotenv configuration sources.
|
Package dotenv provides strict, bounded, optionally interpolated dotenv configuration sources. |
|
Package environment maps explicit environment snapshots into typed trees.
|
Package environment maps explicit environment snapshots into typed trees. |
|
examples
|
|
|
discovery
command
|
|
|
quickstart
command
|
|
|
testing
command
|
|
|
Package filesystem dispatches explicit and discovered files to strict format sources while preserving path provenance.
|
Package filesystem dispatches explicit and discovered files to strict format sources while preserving path provenance. |
|
internal
|
|
|
safeerror
Package safeerror preserves error identity without exposing arbitrary error text through an unwrap chain.
|
Package safeerror preserves error identity without exposing arbitrary error text through an unwrap chain. |
|
sourceio
Package sourceio provides immutable, bounded byte and fs.FS inputs for format source packages.
|
Package sourceio provides immutable, bounded byte and fs.FS inputs for format source packages. |
|
Package json provides strict, bounded JSON configuration sources.
|
Package json provides strict, bounded JSON configuration sources. |
|
Package merge combines configuration trees without mutating its inputs.
|
Package merge combines configuration trees without mutating its inputs. |
|
Package programmatic provides immutable map-backed configuration sources.
|
Package programmatic provides immutable map-backed configuration sources. |
|
Package toml provides strict, bounded TOML configuration sources.
|
Package toml provides strict, bounded TOML configuration sources. |
|
Package validation orchestrates small post-decode configuration validators.
|
Package validation orchestrates small post-decode configuration validators. |
|
Package yaml provides strict, bounded YAML configuration sources.
|
Package yaml provides strict, bounded YAML configuration sources. |