Documentation
¶
Overview ¶
Package config provides configuration loading, merging, and access via the Containable interface backed by Viper.
Configurations can be loaded from multiple sources — local files, embedded assets, environment variables, and command-line flags — and merged with deterministic precedence. Factory functions include NewFilesContainer, LoadFilesContainer, NewReaderContainer, and NewContainerFromViper.
Type-safe accessors (GetString, GetInt, GetBool, GetDuration, GetTime, etc.) are exposed through Containable. For advanced use cases, [Containable.GetViper] provides direct access to the underlying Viper instance as an intentional power-user escape hatch.
Hot-reload is supported via the Observable interface, which allows consumers to register callbacks that fire when configuration files change on disk.
Index ¶
- Variables
- func LoadEnv(fs afero.Fs, logger logger.Logger)
- type Containable
- func Load(paths []string, fs afero.Fs, allowEmptyConfig bool, opts ...ContainerOption) (Containable, error)
- func LoadEmbed(paths []string, assets fs.FS, opts ...ContainerOption) (Containable, error)
- func LoadFilesContainer(fs afero.Fs, opts ...ContainerOption) (Containable, error)
- func LoadFilesContainerWithSchema(fs afero.Fs, schema *Schema, opts ...ContainerOption) (Containable, error)
- type Container
- func (c *Container) AddObserver(o Observable)
- func (c *Container) AddObserverFunc(f func(Containable, chan error))
- func (c *Container) Dump(w io.Writer)
- func (c *Container) Get(key string) any
- func (c *Container) GetBool(key string) bool
- func (c *Container) GetDuration(key string) time.Duration
- func (c *Container) GetFloat(key string) float64
- func (c *Container) GetInt(key string) int
- func (c *Container) GetObservers() []Observable
- func (c *Container) GetString(key string) string
- func (c *Container) GetTime(key string) time.Time
- func (c *Container) GetViper() *viper.Viper
- func (c *Container) Has(key string) bool
- func (c *Container) IsSet(key string) bool
- func (c *Container) Set(key string, value any)
- func (c *Container) SetSchema(schema *Schema)
- func (c *Container) Sub(key string) Containable
- func (c *Container) ToJSON() string
- func (c *Container) Validate(schema *Schema) *ValidationResult
- func (c *Container) WriteConfigAs(dest string) error
- type ContainerOption
- func WithConfigFiles(files ...string) ContainerOption
- func WithConfigFormat(format string) ContainerOption
- func WithConfigReaders(readers ...io.Reader) ContainerOption
- func WithEnvPrefix(prefix string) ContainerOption
- func WithLogger(l logger.Logger) ContainerOption
- func WithSchema(schema *Schema) ContainerOption
- type FieldSchema
- type Observable
- type Observer
- type Schema
- type SchemaOption
- type ValidationError
- type ValidationResult
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var (
ErrNoFilesFound = errors.Newf("no configuration files found please run init, or provide a config file using the --config flag")
)
Functions ¶
Types ¶
type Containable ¶
type Containable interface {
Get(key string) any
GetBool(key string) bool
GetInt(key string) int
GetFloat(key string) float64
GetString(key string) string
GetTime(key string) time.Time
GetDuration(key string) time.Duration
// GetViper returns the underlying Viper instance for advanced operations
// not exposed by the Containable interface. This is an intentional escape
// hatch for power users who need Viper's full API (e.g., MergeConfig,
// BindPFlag, or direct access to config file watching).
//
// Prefer the typed accessor methods (GetString, GetInt, etc.) for standard
// configuration reads. Use GetViper only when the Containable interface
// does not cover your use case.
GetViper() *viper.Viper
Has(key string) bool
IsSet(key string) bool
Set(key string, value any)
WriteConfigAs(dest string) error
Sub(key string) Containable
AddObserver(o Observable)
AddObserverFunc(f func(Containable, chan error))
ToJSON() string
Dump(w io.Writer)
// Validate checks the container's current values against the provided schema.
// Returns a ValidationResult; callers should check result.Valid().
Validate(schema *Schema) *ValidationResult
}
Containable is the primary configuration interface. It provides typed accessors, observation for hot-reload, and schema validation.
func Load ¶
func Load(paths []string, fs afero.Fs, allowEmptyConfig bool, opts ...ContainerOption) (Containable, error)
Load reads configuration from the first available file in paths. Returns ErrNoFilesFound if no files exist and allowEmptyConfig is false.
func LoadEmbed ¶
func LoadEmbed(paths []string, assets fs.FS, opts ...ContainerOption) (Containable, error)
LoadEmbed reads configuration from embedded filesystem assets and merges them.
func LoadFilesContainer ¶
func LoadFilesContainer(fs afero.Fs, opts ...ContainerOption) (Containable, error)
LoadFilesContainer loads configuration from files and returns a Containable. Config files are specified via WithConfigFiles. It returns an error if the first file specified does not exist.
func LoadFilesContainerWithSchema ¶ added in v1.8.0
func LoadFilesContainerWithSchema(fs afero.Fs, schema *Schema, opts ...ContainerOption) (Containable, error)
LoadFilesContainerWithSchema loads config files and validates against the schema. Returns an error wrapping all validation errors if the config is invalid. The schema can also be provided via WithSchema; if both are present, the option takes precedence.
type Container ¶
type Container struct {
ID string
// contains filtered or unexported fields
}
Container container for configuration.
func NewContainerFromViper ¶
NewContainerFromViper creates a new Container from an existing Viper instance. If l is nil, a no-op logger is used.
func NewFilesContainer ¶
func NewFilesContainer(fs afero.Fs, opts ...ContainerOption) *Container
NewFilesContainer initialises a configuration container to read files from the FS. Config files are specified via WithConfigFiles.
func NewReaderContainer ¶
func NewReaderContainer(fs afero.Fs, opts ...ContainerOption) *Container
NewReaderContainer initialises a configuration container to read config from io.Readers. Readers are specified via WithConfigReaders; format via WithConfigFormat.
Example ¶
package main
import (
"fmt"
"strings"
"github.com/spf13/afero"
"github.com/phpboyscout/go-tool-base/pkg/config"
"github.com/phpboyscout/go-tool-base/pkg/logger"
)
func main() {
l := logger.NewNoop()
yaml := `
log:
level: debug
server:
port: 8080
`
cfg := config.NewReaderContainer(afero.NewMemMapFs(), config.WithLogger(l), config.WithConfigFormat("yaml"), config.WithConfigReaders(strings.NewReader(yaml)))
fmt.Println("Level:", cfg.GetString("log.level"))
fmt.Println("Port:", cfg.GetInt("server.port"))
}
Output: Level: debug Port: 8080
func (*Container) AddObserver ¶
func (c *Container) AddObserver(o Observable)
AddObserver attach observer to trigger on config update.
func (*Container) AddObserverFunc ¶
func (c *Container) AddObserverFunc(f func(Containable, chan error))
AddObserverFunc attach function to trigger on config update.
func (*Container) GetDuration ¶
GetDuration get duration value from config.
func (*Container) GetObservers ¶
func (c *Container) GetObservers() []Observable
GetObservers retrieve all currently attached Observers.
func (*Container) SetSchema ¶ added in v1.8.0
SetSchema attaches a validation schema to the container. When set, hot-reload will validate config changes before notifying observers.
func (*Container) Sub ¶
func (c *Container) Sub(key string) Containable
Sub returns a subtree of the parent configuration.
func (*Container) Validate ¶ added in v1.8.0
func (c *Container) Validate(schema *Schema) *ValidationResult
Validate checks the current configuration against the provided schema. Returns a ValidationResult; callers should check result.Valid().
Example ¶
package main
import (
"fmt"
"strings"
"github.com/spf13/afero"
"github.com/phpboyscout/go-tool-base/pkg/config"
"github.com/phpboyscout/go-tool-base/pkg/logger"
)
func main() {
type AppConfig struct {
Log struct {
Level string `config:"log.level" enum:"debug,info,warn,error"`
}
}
l := logger.NewNoop()
cfg := config.NewReaderContainer(afero.NewMemMapFs(), config.WithLogger(l), config.WithConfigFormat("yaml"), config.WithConfigReaders(strings.NewReader("log:\n level: verbose\n")))
schema, _ := config.NewSchema(config.WithStructSchema(AppConfig{}))
result := cfg.Validate(schema)
if !result.Valid() {
fmt.Println(result.Error())
}
}
Output:
func (*Container) WriteConfigAs ¶
WriteConfigAs writes the current configuration to the given path.
type ContainerOption ¶ added in v1.11.0
type ContainerOption func(*containerOptions)
ContainerOption configures optional behavior for config containers.
func WithConfigFiles ¶ added in v1.11.0
func WithConfigFiles(files ...string) ContainerOption
WithConfigFiles specifies one or more config file paths to load. The first file is treated as the primary config; subsequent files are merged in order.
func WithConfigFormat ¶ added in v1.11.0
func WithConfigFormat(format string) ContainerOption
WithConfigFormat sets the config format (e.g. "yaml", "json") for reader-based config loading.
func WithConfigReaders ¶ added in v1.11.0
func WithConfigReaders(readers ...io.Reader) ContainerOption
WithConfigReaders provides one or more io.Readers as config sources. The first reader is the primary config; subsequent readers are merged. Requires WithConfigFormat to be set.
func WithEnvPrefix ¶ added in v1.11.0
func WithEnvPrefix(prefix string) ContainerOption
WithEnvPrefix sets the environment variable prefix for automatic env binding. When set to "GTB", the config key "ai.provider" resolves from the environment variable "GTB_AI_PROVIDER". An empty string disables prefixing (the default, preserving backward compatibility).
func WithLogger ¶ added in v1.11.0
func WithLogger(l logger.Logger) ContainerOption
WithLogger sets the logger for the config container. When not provided, a no-op logger is used.
func WithSchema ¶ added in v1.11.0
func WithSchema(schema *Schema) ContainerOption
WithSchema attaches a validation schema to the container.
type FieldSchema ¶ added in v1.8.0
type FieldSchema struct {
// Type is the expected Go type: "string", "int", "float64", "bool", "duration".
Type string
// Required indicates the field must be present and non-zero.
Required bool
// Description is used in validation error messages.
Description string
// Default is the default value for documentation and error hints only.
// The validation layer does not inject defaults — use embedded assets for that.
Default any
// Enum restricts the field to a set of allowed values.
Enum []any
// Children defines nested fields for map/object types.
Children map[string]FieldSchema
}
FieldSchema describes a single configuration field.
type Observable ¶
type Observable interface {
Run(Containable, chan error)
}
Observable is the interface for config change observers. Implementations receive the updated config and an error channel when the config file changes.
type Observer ¶
type Observer struct {
// contains filtered or unexported fields
}
Observer is a simple Observable that wraps a handler function.
func (Observer) Run ¶
func (o Observer) Run(c Containable, errs chan error)
Run invokes the observer's handler with the updated config.
type Schema ¶ added in v1.8.0
type Schema struct {
// contains filtered or unexported fields
}
Schema defines the expected structure and constraints for configuration values.
func NewSchema ¶ added in v1.8.0
func NewSchema(opts ...SchemaOption) (*Schema, error)
NewSchema creates a Schema from the provided options.
Example ¶
package main
import (
"fmt"
"github.com/phpboyscout/go-tool-base/pkg/config"
)
func main() {
type AppConfig struct {
Server struct {
Port int `config:"server.port" default:"8080"`
Host string `config:"server.host"`
}
Log struct {
Level string `config:"log.level" enum:"debug,info,warn,error" default:"info"`
}
Github struct {
Token string `config:"github.token" validate:"required"`
}
}
schema, err := config.NewSchema(config.WithStructSchema(AppConfig{}))
if err != nil {
fmt.Println("Error:", err)
return
}
_ = schema // Use with container.Validate(schema)
}
Output:
func (*Schema) Fields ¶ added in v1.8.0
func (s *Schema) Fields() map[string]FieldSchema
Fields returns the schema field definitions.
type SchemaOption ¶ added in v1.8.0
type SchemaOption func(*schemaConfig)
SchemaOption configures schema construction.
func WithStrictMode ¶ added in v1.8.0
func WithStrictMode() SchemaOption
WithStrictMode treats unknown keys as errors instead of warnings.
func WithStructSchema ¶ added in v1.8.0
func WithStructSchema(v any) SchemaOption
WithStructSchema derives a schema from a tagged Go struct. Supported tags: `config:"key" validate:"required" enum:"a,b,c" default:"value"`.
type ValidationError ¶ added in v1.8.0
type ValidationError struct {
// Key is the dot-separated config key.
Key string
// Message is a human-readable description of the failure.
Message string
// Hint is an actionable fix suggestion.
Hint string
}
ValidationError contains details about a single validation failure.
func (ValidationError) String ¶ added in v1.8.0
func (e ValidationError) String() string
type ValidationResult ¶ added in v1.8.0
type ValidationResult struct {
Errors []ValidationError
Warnings []ValidationError
}
ValidationResult holds the outcome of schema validation.
func (*ValidationResult) Error ¶ added in v1.8.0
func (r *ValidationResult) Error() string
Error returns a formatted multi-line error string, or empty string if valid.
func (*ValidationResult) Valid ¶ added in v1.8.0
func (r *ValidationResult) Valid() bool
Valid returns true if no errors were found. Warnings do not affect validity.