config

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 15 Imported by: 0

README

Configuration Package

GoDoc Go Report Card Test

github.com/renevo/config provides a typed configuration tree with explicit lifecycle, source precedence, validation, snapshots, and runtime locking.

Lifecycle

Register the schema before loading values:

settings := config.NewSet()
port := settings.Setting("HTTP.Port", 8080, "HTTP listener port", config.Lockable())
settings.Setting("Debug.Enabled", false, "Enable debug behavior")

if err := settings.Load(ctx, config.ValuesSource("file", values)); err != nil {
	panic(err)
}

// Freeze infrastructure-sensitive settings after startup.
settings.Lock()

Load succeeds once. Reload rebuilds effective values from defaults and the provided sources. Apply updates several settings from explicit strings as one transaction. Update and Setting.Set update one setting.

Failed loads, reloads, and applications do not partially update settings, bound fields, revisions, or notifications. A reload with no effective changes does not increment the revision or emit an event.

Sources and precedence

Sources are applied in argument order after registered defaults; later sources override earlier sources. An omitted source value does not clear a default, while an explicit empty string is still a value. Unknown source paths return an error.

Custom sources receive a replayable iter.Seq[config.SettingMetadata] captured at the beginning of Load or Reload. The sequence exposes registered schema metadata by value in unspecified order; it does not expose current values, defaults, or mutable settings:

source := config.SourceFunc(func(ctx context.Context, settings iter.Seq[config.SettingMetadata]) ([]config.RawValue, error) {
	var values []config.RawValue
	for setting := range settings {
		// Use setting.Path and other metadata to discover source values.
	}
	return values, nil
})

EnvironmentSource reads environment variables for the registered settings supplied when it loads. Setting paths are uppercased and non-alphanumeric separators become underscores, so HTTP.Server.Read-Timeout maps to MYAPP_HTTP_SERVER_READ_TIMEOUT with the MYAPP prefix:

settings.Setting("HTTP.Server.Read-Timeout", 15*time.Second, "HTTP read timeout")

if err := settings.Load(ctx, fileSource, config.EnvironmentSource("MYAPP")); err != nil {
	panic(err)
}

Prefixes are uppercased but otherwise preserved. They may contain ASCII letters, digits, and underscores, and a non-empty prefix must start with a letter or underscore. For example, __M__ produces __M___HTTP_SERVER_READ_TIMEOUT. Use an empty prefix to read names such as HTTP_SERVER_READ_TIMEOUT. Ambiguous path mappings fail loading instead of choosing one setting.

Use a named RuntimeSource when overrides should survive later reloads:

runtime := config.NewRuntimeSource("runtime")
_ = runtime.Set("HTTP.Port", "8080")
_ = settings.Load(ctx, fileSource, runtime)
_ = settings.Reload(ctx, fileSource, runtime)

Paths

Paths are case-insensitive at input boundaries and have one canonical spelling, similar to HTTP header canonicalization. Each dotted segment starts with an uppercase character and otherwise uses lowercase characters, with hyphenated segments capitalized after hyphens. For example, http.port, HTTP.PORT, and Http.Port all resolve to Http.Port.

Locking

Lock is irreversible and applies to the root configuration tree. It freezes schema registration and prevents changes to settings registered with config.Lockable(). Non-lockable settings may still be reloaded or updated. There is no Unlock. Direct Setting.Set calls enforce the same lock rule as Apply and Reload.

Binding

Binding is an error-returning adapter for startup configuration structs:

var application struct {
	HTTP struct {
		Port int `setting:"Port" description:"HTTP listener port"`
	}
}

if err := settings.Bind(&application); err != nil {
	panic(err)
}

The package owns bound fields after binding and may update them through setting operations. Application code must not write bound fields directly. Direct struct reads are suitable for startup/read-only use; use Snapshot or typed setting retrieval for runtime consumers that need synchronized reads.

Validation and snapshots

Use WithValidator for per-setting checks and AddValidator for rules over a complete candidate configuration. Validation occurs before a transaction commits and multiple failures are returned together.

Snapshot captures all settings at one revision. Value[T] performs strict typed retrieval and returns an error for a type mismatch:

snapshot, err := settings.Snapshot()
portValue, err := config.Value[int](port)

Encoding and notifications

Built-in codecs support scalar values, named primitive types, time.Duration, and standard encoding.TextMarshaler/ encoding.TextUnmarshaler implementations. Explicit codecs are preferred for composite values. Change subscribers receive stable old/new string values and the committed revision. Callbacks run after internal locks are released; callback panics are not recovered.

Stability

The package is undergoing a deliberate breaking redesign. The API is expected to stabilize at 1.0.0.

Examples

Examples are provided in the documentation, more will be added in the future.

Documentation

Overview

Package config provides a hierarchical, typed configuration system with explicit loading, validation, snapshots, notifications, and runtime locks.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFound reports that a configuration setting or path does not exist.
	ErrNotFound = errors.New("configuration setting not found")
	// ErrAlreadyExists reports an attempt to register a duplicate setting.
	ErrAlreadyExists = errors.New("configuration setting already exists")
	// ErrInvalidPath reports a malformed configuration path.
	ErrInvalidPath = errors.New("invalid configuration path")
	// ErrNotLoaded reports an operation that requires an initial load.
	ErrNotLoaded = errors.New("configuration has not been loaded")
	// ErrAlreadyLoaded reports a second call to Load.
	ErrAlreadyLoaded = errors.New("configuration has already been loaded")
	// ErrLocked reports an attempted change to a locked setting.
	ErrLocked = errors.New("configuration setting is locked")
	// ErrSchemaLocked reports an attempt to change a locked schema.
	ErrSchemaLocked = errors.New("configuration schema is locked")
	// ErrUnsupported reports an unsupported setting type or operation.
	ErrUnsupported = errors.New("unsupported configuration type")
)

Functions

func Value

func Value[T any](setting *Setting) (T, error)

Value returns a setting's value when its dynamic type exactly matches T.

func WithContext added in v0.3.0

func WithContext(ctx context.Context, set *Set) context.Context

WithContext returns a child context carrying set for use with FromContext.

Types

type BindOption added in v0.3.0

type BindOption func(*BindOptions)

BindOption customizes struct binding.

func FlattenAnonymous added in v0.3.0

func FlattenAnonymous() BindOption

FlattenAnonymous makes binding flatten anonymous nested structs.

type BindOptions added in v0.3.0

type BindOptions struct {
	// FlattenAnonymous binds fields of anonymous nested structs into the parent set.
	FlattenAnonymous bool
}

BindOptions configures how a struct is bound to a Set.

type Candidate added in v0.3.0

type Candidate struct {
	// contains filtered or unexported fields
}

Candidate exposes the complete parsed configuration candidate to set validators.

func (Candidate) Get added in v0.3.0

func (c Candidate) Get(path string) (any, bool)

Get returns a candidate value by canonical or case-insensitive path.

type Change added in v0.3.0

type Change struct {
	// Path is the canonical path of the changed setting.
	Path string
	// Old is the previous formatted value.
	Old string
	// New is the committed formatted value.
	New string
	// Revision identifies the configuration revision containing the change.
	Revision uint64
}

Change describes one committed setting change.

type ChangeFunc added in v0.3.0

type ChangeFunc func(Change)

ChangeFunc adapts a function to ChangeNotifier.

func (ChangeFunc) NotifyChange added in v0.3.0

func (f ChangeFunc) NotifyChange(change Change)

NotifyChange implements ChangeNotifier.

type ChangeNotifier added in v0.3.0

type ChangeNotifier interface {
	// NotifyChange receives a committed setting change.
	NotifyChange(Change)
}

ChangeNotifier receives stable change records after a commit.

type Codec added in v0.3.0

type Codec interface {
	// Parse converts text into the setting's value type.
	Parse(string) (any, error)
	// Format converts a setting value to text.
	Format(any) (string, error)
	// Equal reports whether two values represent the same setting value.
	Equal(any, any) bool
}

Codec converts a setting value to and from its text representation.

Implementations can add support for application-specific setting types.

type LockedError added in v0.3.0

type LockedError struct {
	// Path is the canonical path of the locked setting.
	Path string
}

LockedError identifies a lockable setting that could not be changed.

func (*LockedError) Error added in v0.3.0

func (e *LockedError) Error() string

Error returns the locked setting error message.

func (*LockedError) Unwrap added in v0.3.0

func (e *LockedError) Unwrap() error

Unwrap returns ErrLocked.

type Notifier

type Notifier interface {
	// Notify receives the changed setting.
	Notify(s *Setting)
}

Notifier receives a setting after its value changes.

type NotifyFunc

type NotifyFunc func(s *Setting)

NotifyFunc adapts a function to Notifier.

func (NotifyFunc) Notify

func (f NotifyFunc) Notify(s *Setting)

Notify implements Notifier.Notify

type NotifyHandle

type NotifyHandle struct {
	// contains filtered or unexported fields
}

NotifyHandle is used to stop notifications of Setting changes

func (*NotifyHandle) Close

func (h *NotifyHandle) Close() error

Close stops notifications. Calling Close more than once is safe.

type RawValue added in v0.3.0

type RawValue struct {
	// Path is the source-provided setting path.
	Path string
	// Value is the unparsed setting text. An empty value is explicit.
	Value string
	// Source identifies the source for diagnostics.
	Source string
}

RawValue is one source-provided configuration value.

type RuntimeSource added in v0.3.0

type RuntimeSource struct {
	// contains filtered or unexported fields
}

RuntimeSource is a named, mutable source layer intended to survive reloads.

func NewRuntimeSource added in v0.3.0

func NewRuntimeSource(name string) *RuntimeSource

NewRuntimeSource creates a named runtime override source.

func (*RuntimeSource) Delete added in v0.3.0

func (source *RuntimeSource) Delete(path string) error

Delete removes an override from the runtime source.

func (*RuntimeSource) Load added in v0.3.0

Load implements Source.

func (*RuntimeSource) Set added in v0.3.0

func (source *RuntimeSource) Set(path, value string) error

Set assigns an override in the runtime source.

type Set

type Set struct {
	// contains filtered or unexported fields
}

Set is a hierarchical collection of configuration settings.

func FromContext

func FromContext(ctx context.Context) *Set

FromContext returns the set stored in ctx, or nil when no set is stored.

func NewSet

func NewSet() *Set

NewSet creates an independent root configuration set.

func (*Set) AddValidator added in v0.3.0

func (s *Set) AddValidator(validator SetValidator) error

AddValidator adds a validator for complete configuration candidates.

func (*Set) Apply added in v0.3.0

func (s *Set) Apply(values map[string]string) error

Apply parses, validates, and commits multiple setting updates atomically.

func (*Set) Bind

func (s *Set) Bind(value any, bindOptions ...BindOption) error

Bind binds a pointer to a struct into s.

Fields can use setting, description, and mask tags. Bound fields are package-owned after binding and should be treated as read-only by callers.

Example
package main

import (
	"os"

	"github.com/renevo/config"
)

func main() {
	settings := config.NewSet()

	// create just a simple struct
	myConfig := struct {
		Name     string `description:"This is a name"`
		Password string `description:"Super secret password" mask:"true"`
		HTTP     struct {
			Addr string `setting:"Address" description:"Address to listen"`
			Port int16  `description:"What port to listen"`
		}
		Enabled bool `description:"Enable something"`
	}{
		Name: "Default User",
	}

	// set values like normal
	myConfig.HTTP.Addr = "0.0.0.0"
	myConfig.HTTP.Port = 8080

	// bind the configuration under MyApplication to the pointer of the config
	applicationSet := settings.Subset("MyApplication")
	if applicationSet == nil {
		panic("unable to create application configuration set")
	}
	bindErr := applicationSet.Bind(&myConfig)
	if bindErr != nil {
		panic(bindErr)
	}

	// manually update a setting by full path (the value being set can come from os.GetEnv())
	_, _ = settings.Update("MyApplication.Enabled", "true")

	// dump the output
	_ = settings.Dump(os.Stdout)

}
Output:
Path                           Type        Value              Default Value      Description
Myapplication.Enabled          *bool       "true"             "false"            Enable something
Myapplication.Http.Address     *string     "0.0.0.0"          "0.0.0.0"          Address to listen
Myapplication.Http.Port        *int16      "8080"             "8080"             What port to listen
Myapplication.Name             *string     "Default User"     "Default User"     This is a name
Myapplication.Password         *string     "*****"            "*****"            Super secret password

func (*Set) Dump

func (s *Set) Dump(w io.Writer) error

Dump writes the current settings as a tab-separated report.

func (*Set) Get

func (s *Set) Get(name string) *Setting

Get returns a setting by relative-first, case-insensitive path.

func (*Set) Load added in v0.3.0

func (s *Set) Load(ctx context.Context, sources ...Source) error

Load establishes the initial configuration from defaults and sources.

func (*Set) Loaded added in v0.3.0

func (s *Set) Loaded() bool

Loaded reports whether the initial configuration has been loaded.

func (*Set) Lock added in v0.3.0

func (s *Set) Lock()

Lock freezes the schema and prevents changes to lockable settings.

func (*Set) Locked added in v0.3.0

func (s *Set) Locked() bool

Locked reports whether the root configuration tree is locked.

func (*Set) Lookup added in v0.3.0

func (s *Set) Lookup(path string) (*Setting, error)

Lookup finds a setting using relative-first, case-insensitive path lookup.

func (*Set) LookupAbsolute added in v0.3.0

func (s *Set) LookupAbsolute(path string) (*Setting, error)

LookupAbsolute finds a setting from the root configuration path.

func (*Set) LookupRelative added in v0.3.0

func (s *Set) LookupRelative(name string) (*Setting, error)

LookupRelative finds a setting relative to s.

func (*Set) Name

func (s *Set) Name() string

Name returns the canonical name of the set.

func (*Set) Notify

func (s *Set) Notify(n Notifier) *NotifyHandle

Notify subscribes to setting additions and changes in s and its children.

func (*Set) Parent

func (s *Set) Parent() *Set

Parent returns the parent set, or s for a root set.

func (*Set) Path

func (s *Set) Path() string

Path returns the canonical path of the set.

func (*Set) Range

func (s *Set) Range(fn func(string, *Setting) bool)

Range visits settings below s in canonical path order-independent registry order.

func (*Set) Reload added in v0.3.0

func (s *Set) Reload(ctx context.Context, sources ...Source) error

Reload replaces the loaded configuration from defaults and sources atomically.

func (*Set) Revision added in v0.3.0

func (s *Set) Revision() uint64

Revision returns the committed revision number of the root configuration.

func (*Set) Root

func (s *Set) Root() *Set

Root returns the root configuration set.

func (*Set) Setting

func (s *Set) Setting(name string, value any, description string, options ...SettingOption) *Setting

Setting registers a setting in s. The name must not be empty and value must not be nil.

func (*Set) Snapshot added in v0.3.0

func (s *Set) Snapshot() (Snapshot, error)

Snapshot returns a coherent view of the configuration at one revision.

func (*Set) Subset

func (s *Set) Subset(name string) *Set

Subset returns an existing child set or creates a new child set.

func (*Set) Update

func (s *Set) Update(name, value string) (bool, error)

Update parses and updates one setting. The boolean reports whether the path was found.

func (*Set) Validate added in v0.3.0

func (s *Set) Validate() error

Validate checks the currently committed configuration.

type SetValidator added in v0.3.0

type SetValidator func(Candidate) error

SetValidator checks a complete candidate configuration before it commits.

type Setting

type Setting struct {
	// Mask causes String and snapshots to hide the current and default values.
	Mask bool

	// Name is the local name of the setting.
	Name string

	// Description is user-facing text suitable for help output.
	Description string

	// DefaultValue is the formatted value used before sources are applied.
	DefaultValue string

	// Path is the canonical dot-separated path of the setting.
	Path string

	// Lockable marks a setting that cannot change after its owning set is locked.
	Lockable bool
	// contains filtered or unexported fields
}

Setting describes one registered configuration value.

func (*Setting) Equals

func (s *Setting) Equals(text string) bool

Equals reports whether text represents the current value.

func (*Setting) IsDefault

func (s *Setting) IsDefault() bool

IsDefault reports whether the current value equals DefaultValue.

func (*Setting) Notify

func (s *Setting) Notify(n Notifier) *NotifyHandle

Notify subscribes to setting change notifications.

func (*Setting) NotifyChange added in v0.3.0

func (s *Setting) NotifyChange(n ChangeNotifier) *NotifyHandle

NotifyChange subscribes to stable old/new change records.

func (*Setting) Set

func (s *Setting) Set(text string) error

Set parses and commits a new value from text.

func (*Setting) String

func (s *Setting) String() string

String formats the current value, masking it when Mask is true.

func (*Setting) ValueType added in v0.4.0

func (s *Setting) ValueType() reflect.Type

ValueType returns the reflect.Type of the underlying value.

type SettingError added in v0.3.0

type SettingError struct {
	// Path is the canonical setting path associated with the error.
	Path string
	// Source identifies the source that produced the error, when applicable.
	Source string
	// Err is the underlying cause.
	Err error
}

SettingError adds path and source context to an underlying error.

func (*SettingError) Error added in v0.3.0

func (e *SettingError) Error() string

Error returns the contextual error message.

func (*SettingError) Unwrap added in v0.3.0

func (e *SettingError) Unwrap() error

Unwrap returns the underlying cause.

type SettingMetadata added in v0.6.0

type SettingMetadata struct {
	// Path is the canonical setting path.
	Path string
	// Name is the setting name relative to its containing set.
	Name string
	// Description is the setting's user-facing description.
	Description string
	// Type is the registered Go type.
	Type reflect.Type
	// Masked reports whether the setting hides its value.
	Masked bool
	// Lockable reports whether locking the set prevents changes to the setting.
	Lockable bool
}

SettingMetadata describes a registered setting without exposing its value or mutation APIs.

type SettingOption added in v0.3.0

type SettingOption func(*Setting)

SettingOption configures a setting at registration time.

func Lockable added in v0.3.0

func Lockable() SettingOption

Lockable marks a setting as protected by Set.Lock.

func WithCodec added in v0.3.0

func WithCodec(codec Codec) SettingOption

WithCodec uses codec to parse, format, and compare a setting value.

func WithLockable added in v0.3.0

func WithLockable(lockable bool) SettingOption

WithLockable sets whether a setting is protected by Set.Lock.

func WithValidator added in v0.3.0

func WithValidator(validator Validator) SettingOption

WithValidator validates a setting value before a transaction commits.

type SettingSnapshot added in v0.3.0

type SettingSnapshot struct {
	// Path is the canonical setting path.
	Path string
	// Type is the registered Go type.
	Type string
	// Value is the formatted current value, masked when requested.
	Value string
	// DefaultValue is the formatted default value, masked when requested.
	DefaultValue string
	// Description is the setting's user-facing description.
	Description string
	// Masked reports whether the value was masked.
	Masked bool
}

SettingSnapshot is an immutable textual view of one setting at a revision.

type Snapshot added in v0.3.0

type Snapshot struct {
	// Revision is the configuration revision represented by Values.
	Revision uint64
	// Values contains settings sorted by canonical path.
	Values []SettingSnapshot
}

Snapshot contains settings observed at one committed revision.

type Source added in v0.3.0

type Source interface {
	// Load returns the raw values currently supplied by the source. Settings is
	// a replayable, order-unspecified snapshot of registered setting metadata.
	Load(context.Context, iter.Seq[SettingMetadata]) ([]RawValue, error)
}

Source supplies raw configuration values for Load and Reload.

func EnvironmentSource added in v0.6.0

func EnvironmentSource(prefix string) Source

EnvironmentSource reads registered settings from environment variables. Prefix is normalized when the source loads; an invalid prefix fails loading.

func ValuesSource added in v0.3.0

func ValuesSource(name string, values map[string]string) Source

ValuesSource creates a source from a map. Map iteration order does not affect precedence.

type SourceFunc added in v0.3.0

type SourceFunc func(context.Context, iter.Seq[SettingMetadata]) ([]RawValue, error)

SourceFunc adapts a function to Source.

func (SourceFunc) Load added in v0.3.0

func (source SourceFunc) Load(ctx context.Context, settings iter.Seq[SettingMetadata]) ([]RawValue, error)

Load implements Source.

type Validator added in v0.3.0

type Validator func(any) error

Validator checks one setting's parsed value before a transaction commits.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL