Documentation
¶
Overview ¶
Package config provides configuration management for Go applications.
The config package loads configuration data from multiple sources including files, environment variables, and remote systems like Consul. Configuration sources are merged in order, with later sources overriding earlier ones. All configuration keys are case-insensitive.
Key Features ¶
- Multiple configuration sources (files, environment variables, Consul)
- Automatic format detection and decoding (JSON, YAML, TOML)
- Struct binding with automatic type conversion
- Validation using JSON Schema or custom validators
- Case-insensitive key access with dot notation
- Thread-safe configuration loading and access
- Configuration dumping to files or custom destinations
Quick Start ¶
Create a configuration instance with sources:
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithEnv("APP_"),
)
Load the configuration:
if err := cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
Access configuration values:
port := cfg.Int("server.port")
host := cfg.StringOr("server.host", "localhost")
debug := cfg.Bool("debug")
Configuration Sources ¶
The package supports multiple configuration sources that can be combined:
Files with automatic format detection:
config.WithFile("config.yaml") // Detects YAML
config.WithFile("config.json") // Detects JSON
config.WithFile("config.toml") // Detects TOML
Files with explicit format:
config.WithFileAs("config", codec.TypeYAML)
Environment variables with prefix:
config.WithEnv("APP_") // Loads APP_SERVER_PORT as server.port
Consul key-value store:
config.WithConsul("production/service.yaml")
Raw content:
yamlData := []byte("port: 8080")
config.WithContent(yamlData, codec.TypeYAML)
Struct Binding ¶
Bind configuration to a struct for type-safe access:
type AppConfig struct {
Port int `config:"port"`
Host string `config:"host"`
Timeout time.Duration `config:"timeout"`
Debug bool `config:"debug" default:"false"`
}
var appConfig AppConfig
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithBinding(&appConfig),
)
if err := cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
// Access typed fields directly
fmt.Printf("Server: %s:%d\n", appConfig.Host, appConfig.Port)
Validation ¶
Validate configuration using struct methods:
type Config struct {
Port int `config:"port"`
}
func (c *Config) Validate() error {
if c.Port < 1 || c.Port > 65535 {
return fmt.Errorf("port must be between 1 and 65535")
}
return nil
}
Validate using JSON Schema:
schema := []byte(`{
"type": "object",
"properties": {
"port": {"type": "integer", "minimum": 1, "maximum": 65535}
},
"required": ["port"]
}`)
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithJSONSchema(schema),
)
Validate using custom functions:
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithValidator(func(values map[string]any) error {
if port, ok := values["port"].(int); ok && port < 1 {
return fmt.Errorf("invalid port: %d", port)
}
return nil
}),
)
Accessing Configuration Values ¶
Access values using type-specific methods:
// Basic types
port := cfg.Int("server.port")
host := cfg.String("server.host")
debug := cfg.Bool("debug")
rate := cfg.Float64("rate")
// With default values
host := cfg.StringOr("server.host", "localhost")
port := cfg.IntOr("server.port", 8080)
// Collections
tags := cfg.StringSlice("tags")
ports := cfg.IntSlice("ports")
metadata := cfg.StringMap("metadata")
// Time-related
timeout := cfg.Duration("timeout")
startTime := cfg.Time("start_time")
Using generic functions with error handling:
port, err := config.GetE[int](cfg, "server.port")
if err != nil {
log.Fatalf("port configuration required: %v", err)
}
Configuration Dumping ¶
Save the current configuration to a file:
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithFileDumper("output.yaml"),
)
cfg.Load(context.Background())
cfg.Dump(context.Background()) // Writes to output.yaml
Thread Safety ¶
Config is safe for concurrent use by multiple goroutines. Configuration loading and reading are protected by internal locks. Multiple goroutines can safely call Load() and access configuration values simultaneously.
Error Handling ¶
The package provides detailed error information through [ConfigError]:
if err := cfg.Load(ctx); err != nil {
var configErr *config.ConfigError
if errors.As(err, &configErr) {
fmt.Printf("Error in %s during %s: %v\n",
configErr.Source, configErr.Operation, configErr.Err)
}
}
Examples ¶
See the examples directory for complete working examples demonstrating various configuration patterns and use cases including:
- Basic configuration loading from files
- Environment variable overrides
- Struct binding with validation
- JSON Schema validation
- Custom validation functions
- Configuration dumping
- Consul integration
For more details, see the package documentation at https://pkg.go.dev/rivaas.dev/config
Example ¶
Example demonstrates basic configuration usage.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
// Create config with YAML content
yamlContent := []byte(`
server:
host: localhost
port: 8080
database:
name: mydb
`)
cfg, err := config.New(
config.WithContent(yamlContent, codec.TypeYAML),
)
if err != nil {
log.Fatal(err)
}
// Load configuration
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
// Access values
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("server.host"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Int("server.port"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("database.name"))
}
Output: localhost 8080 mydb
Example (EnvironmentVariables) ¶
Example_environmentVariables demonstrates loading configuration from environment variables.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
)
func main() {
// In real usage, set environment variables like:
// export APP_SERVER_HOST=localhost
// export APP_SERVER_PORT=8080
cfg, err := config.New(
config.WithEnv("APP_"),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
// Access environment variables without the prefix
// e.g., APP_SERVER_HOST becomes server.host
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println("Environment variables loaded")
}
Output: Environment variables loaded
Example (MultipleSources) ¶
Example_multipleSources demonstrates merging multiple configuration sources.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
// Base configuration
baseConfig := []byte(`
server:
host: localhost
port: 8080
`)
// Override configuration
overrideConfig := []byte(`
server:
port: 9090
`)
cfg, err := config.New(
config.WithContent(baseConfig, codec.TypeYAML),
config.WithContent(overrideConfig, codec.TypeYAML),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
// Later sources override earlier ones
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("server.host"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Int("server.port"))
}
Output: localhost 9090
Index ¶
- func AssertConfigBool(t *testing.T, cfg *Config, key string, expected bool)
- func AssertConfigInt(t *testing.T, cfg *Config, key string, expected int)
- func AssertConfigString(t *testing.T, cfg *Config, key, expected string)
- func AssertConfigValue(t *testing.T, cfg *Config, key string, expected any)
- func Get[T any](c *Config, key string) T
- func GetE[T any](c *Config, key string) (T, error)
- func GetOr[T any](c *Config, key string, defaultVal T) T
- func TestJSONFile(t *testing.T, content []byte) string
- func TestTOMLFile(t *testing.T, content []byte) string
- func TestYAMLFile(t *testing.T, content []byte) string
- type Config
- func MustNew(options ...Option) *Config
- func New(options ...Option) (*Config, error)
- func TestConfig(t *testing.T, opts ...Option) *Config
- func TestConfigFromJSONFile(t *testing.T, content []byte) *Config
- func TestConfigFromTOMLFile(t *testing.T, content []byte) *Config
- func TestConfigFromYAMLFile(t *testing.T, content []byte) *Config
- func TestConfigLoaded(t *testing.T, conf map[string]any) *Config
- func TestConfigWithBinding(t *testing.T, conf map[string]any, target any) *Config
- func TestConfigWithSource(t *testing.T, conf map[string]any) *Config
- func TestConfigWithValidator(t *testing.T, conf map[string]any, validator func(map[string]any) error) *Config
- func (c *Config) Bool(key string) bool
- func (c *Config) BoolOr(key string, defaultVal bool) bool
- func (c *Config) Dump(ctx context.Context) error
- func (c *Config) Duration(key string) time.Duration
- func (c *Config) DurationOr(key string, defaultVal time.Duration) time.Duration
- func (c *Config) Float64(key string) float64
- func (c *Config) Float64Or(key string, defaultVal float64) float64
- func (c *Config) Get(key string) any
- func (c *Config) Int(key string) int
- func (c *Config) Int64(key string) int64
- func (c *Config) Int64Or(key string, defaultVal int64) int64
- func (c *Config) IntOr(key string, defaultVal int) int
- func (c *Config) IntSlice(key string) []int
- func (c *Config) IntSliceOr(key string, defaultVal []int) []int
- func (c *Config) Load(ctx context.Context) error
- func (c *Config) MustDump(ctx context.Context)
- func (c *Config) MustLoad(ctx context.Context)
- func (c *Config) String(key string) string
- func (c *Config) StringMap(key string) map[string]any
- func (c *Config) StringMapOr(key string, defaultVal map[string]any) map[string]any
- func (c *Config) StringOr(key, defaultVal string) string
- func (c *Config) StringSlice(key string) []string
- func (c *Config) StringSliceOr(key string, defaultVal []string) []string
- func (c *Config) Time(key string) time.Time
- func (c *Config) TimeOr(key string, defaultVal time.Time) time.Time
- func (c *Config) Values() *map[string]any
- type Dumper
- type Error
- type MockCodec
- type MockDecoder
- type MockDumper
- type MockEncoder
- type Option
- func WithBinding(v any) Option
- func WithConsul(path string) Option
- func WithConsulAs(path string, codecType codec.Type) Option
- func WithContent(data []byte, codecType codec.Type) Option
- func WithDumper(dumper Dumper) Option
- func WithEnv(prefix string) Option
- func WithFile(path string) Option
- func WithFileAs(path string, codecType codec.Type) Option
- func WithFileDumper(path string) Option
- func WithFileDumperAs(path string, codecType codec.Type) Option
- func WithJSONSchema(schema []byte) Option
- func WithSource(loader Source) Option
- func WithTag(tagName string) Option
- func WithValidator(fn func(map[string]any) error) Option
- type Source
- type Validator
- type Watcher
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AssertConfigBool ¶
AssertConfigBool asserts that a boolean configuration value matches the expected value.
func AssertConfigInt ¶
AssertConfigInt asserts that an integer configuration value matches the expected value.
func AssertConfigString ¶
AssertConfigString asserts that a string configuration value matches the expected value.
func AssertConfigValue ¶
AssertConfigValue asserts that a configuration value matches the expected value.
func Get ¶
Get returns the value associated with the given key as type T. If the key is not found or cannot be converted to type T, it returns the zero value of T. This generic function is useful for custom types or when you need type-safe access.
Example:
port := config.Get[int](cfg, "server.port") timeout := config.Get[time.Duration](cfg, "timeout") custom := config.Get[MyCustomType](cfg, "custom")
func GetE ¶
GetE returns the value associated with the given key as type T, with error handling. If the key is not found, it returns an error. If the value cannot be converted to type T, it returns an error. This is useful when you need explicit error handling for missing or invalid configuration.
Example:
port, err := config.GetE[int](cfg, "server.port")
if err != nil {
return fmt.Errorf("failed to get port: %w", err)
}
custom, err := config.GetE[MyCustomType](cfg, "custom")
if err != nil {
return fmt.Errorf("failed to get custom config: %w", err)
}
func GetOr ¶
GetOr returns the value associated with the given key as type T. If the key is not found or cannot be converted to type T, it returns the provided default value. The type T is inferred from the default value.
Example:
port := config.GetOr(cfg, "server.port", 8080) // type inferred as int host := config.GetOr(cfg, "server.host", "localhost") // type inferred as string timeout := config.GetOr(cfg, "timeout", 30*time.Second) // type inferred as time.Duration
func TestJSONFile ¶
TestJSONFile creates a temporary JSON file with the given content. The file is automatically cleaned up when the test completes.
func TestTOMLFile ¶
TestTOMLFile creates a temporary TOML file with the given content. The file is automatically cleaned up when the test completes.
Types ¶
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
Config manages configuration data loaded from multiple sources. It provides thread-safe access to configuration values and supports binding to structs, validation, and dumping to files.
Config is safe for concurrent use by multiple goroutines.
func MustNew ¶
MustNew creates a new Config instance with the provided options. It panics if any option returns an error. Use this in main() or initialization code where panic is acceptable. For cases where error handling is needed, use New() instead.
Example ¶
ExampleMustNew demonstrates creating a configuration instance with panic on error.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
)
func main() {
cfg := config.MustNew()
if err := cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println("Config created successfully")
}
Output: Config created successfully
func New ¶
New creates a new Config instance with the provided options. It iterates through the options and applies each one to the Config instance. If any of the options return an error, the errors are collected and returned. Returns a partially initialized Config along with any errors encountered.
Example ¶
ExampleNew demonstrates creating a new configuration instance.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
)
func main() {
cfg, err := config.New()
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println("Config created successfully")
}
Output: Config created successfully
func TestConfig ¶
TestConfig creates a new Config instance with the given options for testing. It fails the test if creation fails.
func TestConfigFromJSONFile ¶
TestConfigFromJSONFile creates a Config instance loaded from a temporary JSON file.
func TestConfigFromTOMLFile ¶
TestConfigFromTOMLFile creates a Config instance loaded from a temporary TOML file.
func TestConfigFromYAMLFile ¶
TestConfigFromYAMLFile creates a Config instance loaded from a temporary YAML file.
func TestConfigLoaded ¶
TestConfigLoaded creates and loads a Config instance with the given configuration. Note: Uses context.Background() for simplicity. For tests needing context control, create the config manually and call Load with t.Context().
func TestConfigWithBinding ¶
TestConfigWithBinding creates a Config instance with the given configuration and binding target.
func TestConfigWithSource ¶
TestConfigWithSource creates a new Config instance with a mock source for testing.
func TestConfigWithValidator ¶
func TestConfigWithValidator(t *testing.T, conf map[string]any, validator func(map[string]any) error) *Config
TestConfigWithValidator creates a Config instance with the given configuration and validator.
func (*Config) Bool ¶
Bool returns the value associated with the given key as a boolean. If the value is not found or cannot be converted to a boolean, false is returned.
Example:
debug := cfg.Bool("debug")
Example ¶
ExampleConfig_Bool demonstrates retrieving boolean values.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
jsonContent := []byte(`{"debug": true, "verbose": false}`)
cfg, err := config.New(
config.WithContent(jsonContent, codec.TypeJSON),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Bool("debug"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Bool("verbose"))
}
Output: true false
func (*Config) BoolOr ¶
BoolOr returns the value associated with the given key as a boolean, or the default value if not found.
Example:
debug := cfg.BoolOr("debug", false)
func (*Config) Dump ¶
Dump writes the current configuration values to the registered dumpers.
Errors:
- Returns error if ctx is nil
- Returns error if any dumper fails to write the configuration
func (*Config) Duration ¶
Duration returns the value associated with the given key as a time.Duration. If the value is not found or cannot be converted to a time.Duration, the zero value is returned.
Example:
timeout := cfg.Duration("timeout")
func (*Config) DurationOr ¶
DurationOr returns the value associated with the given key as a time.Duration, or the default value if not found.
Example:
timeout := cfg.DurationOr("timeout", 30*time.Second)
func (*Config) Float64 ¶
Float64 returns the value associated with the given key as a float64. If the value is not found or cannot be converted to a float64, 0.0 is returned.
Example:
rate := cfg.Float64("rate")
func (*Config) Float64Or ¶
Float64Or returns the value associated with the given key as a float64, or the default value if not found.
Example:
rate := cfg.Float64Or("rate", 0.5)
func (*Config) Get ¶
Get returns the value associated with the given key as an any type. If the key is not found, it returns nil.
Example ¶
ExampleConfig_Get demonstrates retrieving configuration values.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
yamlContent := []byte(`
settings:
enabled: true
count: 42
`)
cfg, err := config.New(
config.WithContent(yamlContent, codec.TypeYAML),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Get("settings.enabled"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Get("settings.count"))
}
Output: true 42
func (*Config) Int ¶
Int returns the value associated with the given key as an int. If the value is not found or cannot be converted to an int, 0 is returned.
Example:
port := cfg.Int("server.port")
Example ¶
ExampleConfig_Int demonstrates retrieving integer values.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
jsonContent := []byte(`{"port": 8080, "workers": 4}`)
cfg, err := config.New(
config.WithContent(jsonContent, codec.TypeJSON),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Int("port"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.Int("workers"))
}
Output: 8080 4
func (*Config) Int64 ¶
Int64 returns the value associated with the given key as an int64. If the value is not found or cannot be converted to an int64, 0 is returned.
Example:
maxSize := cfg.Int64("max_size")
func (*Config) Int64Or ¶
Int64Or returns the value associated with the given key as an int64, or the default value if not found.
Example:
maxSize := cfg.Int64Or("max_size", 1024)
func (*Config) IntOr ¶
IntOr returns the value associated with the given key as an int, or the default value if not found.
Example:
port := cfg.IntOr("server.port", 8080)
func (*Config) IntSlice ¶
IntSlice returns the value associated with the given key as a slice of integers. If the value is not found or cannot be converted to a slice of integers, an empty slice is returned.
Example:
ports := cfg.IntSlice("ports")
func (*Config) IntSliceOr ¶
IntSliceOr returns the value associated with the given key as a slice of integers, or the default value if not found.
Example:
ports := cfg.IntSliceOr("ports", []int{8080, 8081})
func (*Config) Load ¶
Load loads configuration data from the registered sources and merges it into the internal values map. The method validates the configuration data before atomically updating the internal state. Load is safe to call concurrently.
Errors:
- Returns error if ctx is nil
- Returns [ConfigError] if any source fails to load
- Returns [ConfigError] if JSON schema validation fails
- Returns [ConfigError] if custom validators fail
- Returns [ConfigError] if binding or struct validation fails
func (*Config) MustDump ¶ added in v0.2.0
MustDump writes configuration to dumpers or panics on error. This is a convenience wrapper around Dump for use cases where dump failure should halt the program.
For error handling, use Dump instead.
Example:
cfg := config.MustNew(
config.WithDumper(myDumper),
)
cfg.MustDump(context.Background())
Example ¶
ExampleConfig_MustDump demonstrates dumping configuration with panic on error.
package main
import (
"context"
"fmt"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
// Create a mock dumper for demonstration
dumper := &config.MockDumper{}
cfg := config.MustNew(
config.WithContent([]byte(`{"service": "api", "version": "1.0"}`), codec.TypeJSON),
config.WithDumper(dumper),
)
cfg.MustLoad(context.Background())
// MustDump will panic if dumping fails - appropriate for startup/shutdown code
cfg.MustDump(context.Background())
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println("Configuration dumped successfully")
}
Output: Configuration dumped successfully
func (*Config) MustLoad ¶ added in v0.2.0
MustLoad loads configuration or panics on error. This is a convenience wrapper around Load for use cases where configuration loading failure should halt the program, typically in main() or init().
For error handling, use Load instead.
Example:
cfg := config.MustNew(
config.WithFile("config.yaml"),
)
cfg.MustLoad(context.Background())
Example ¶
ExampleConfig_MustLoad demonstrates loading configuration with panic on error. This is useful in main() or init() where configuration loading failure should halt the program.
package main
import (
"context"
"fmt"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
cfg := config.MustNew(
config.WithContent([]byte(`{"app": "example", "port": 8080}`), codec.TypeJSON),
)
// MustLoad will panic if loading fails - appropriate for startup code
cfg.MustLoad(context.Background())
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Printf("App: %s, Port: %d\n", cfg.String("app"), cfg.Int("port"))
}
Output: App: example, Port: 8080
func (*Config) String ¶
String returns the value associated with the given key as a string. If the value is not found or cannot be converted to a string, an empty string is returned.
Example:
host := cfg.String("server.host")
Example ¶
ExampleConfig_String demonstrates retrieving string values.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
jsonContent := []byte(`{"name": "MyApp", "env": "production"}`)
cfg, err := config.New(
config.WithContent(jsonContent, codec.TypeJSON),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("name"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("env"))
}
Output: MyApp production
func (*Config) StringMap ¶
StringMap returns the value associated with the given key as a map[string]any. If the value is not found or cannot be converted to a map[string]any, an empty map is returned.
Example:
metadata := cfg.StringMap("metadata")
Example ¶
ExampleConfig_StringMap demonstrates retrieving string maps.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
yamlContent := []byte(`
metadata:
author: John Doe
version: 1.0.0
`)
cfg, err := config.New(
config.WithContent(yamlContent, codec.TypeYAML),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
metadata := cfg.StringMap("metadata")
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(metadata["author"])
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(metadata["version"])
}
Output: John Doe 1.0.0
func (*Config) StringMapOr ¶
StringMapOr returns the value associated with the given key as a map[string]any, or the default value if not found.
Example:
metadata := cfg.StringMapOr("metadata", map[string]any{"version": "1.0"})
func (*Config) StringOr ¶
StringOr returns the value associated with the given key as a string, or the default value if not found.
Example:
host := cfg.StringOr("server.host", "localhost")
func (*Config) StringSlice ¶
StringSlice returns the value associated with the given key as a slice of strings. If the value is not found or cannot be converted to a slice of strings, an empty slice is returned.
Example:
tags := cfg.StringSlice("tags")
Example ¶
ExampleConfig_StringSlice demonstrates retrieving string slices.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
yamlContent := []byte(`
tags:
- web
- api
- backend
`)
cfg, err := config.New(
config.WithContent(yamlContent, codec.TypeYAML),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
tags := cfg.StringSlice("tags")
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Printf("%v\n", tags)
}
Output: [web api backend]
func (*Config) StringSliceOr ¶
StringSliceOr returns the value associated with the given key as a slice of strings, or the default value if not found.
Example:
tags := cfg.StringSliceOr("tags", []string{"default"})
func (*Config) Time ¶
Time returns the value associated with the given key as a time.Time. If the value is not found or cannot be converted to a time.Time, the zero value is returned.
Example:
startTime := cfg.Time("start_time")
type Dumper ¶
type Dumper interface {
// Dump writes the configuration values to a destination.
// The values map should not be modified by implementations.
Dump(ctx context.Context, values *map[string]any) error
}
Dumper defines the interface for configuration dumpers. Implementations write configuration data to various destinations such as files or remote services.
Dump must be safe to call concurrently.
type Error ¶ added in v0.2.0
type Error struct {
Source string // The source where the error occurred (e.g., "source[0]", "json-schema", "binding")
Field string // The specific field where the error occurred (optional)
Operation string // The operation being performed (e.g., "load", "validate", "bind", "merge")
Err error // The underlying error
}
Error ConfigError represents a configuration error with detailed context. It provides information about where the error occurred (source, field), what operation was being performed, and the underlying error.
func NewError ¶ added in v0.2.0
NewError creates a new ConfigError with the provided context. This is a convenience function for creating ConfigError instances.
func NewFieldError ¶ added in v0.2.0
NewFieldError creates a new ConfigError with field information. This is useful when the error is specific to a particular configuration field.
type MockCodec ¶
type MockCodec struct {
MockDecoder
MockEncoder
}
MockCodec is a test codec that implements both Encoder and Decoder.
type MockDecoder ¶
MockDecoder is a test decoder that can be configured to return specific values or errors.
type MockDumper ¶ added in v0.2.0
type MockDumper struct {
// contains filtered or unexported fields
}
MockDumper is a test implementation of the Dumper interface.
func TestDumperWithError ¶
func TestDumperWithError(err error) *MockDumper
TestDumperWithError creates a mock dumper that returns an error on Dump.
type MockEncoder ¶
MockEncoder is a test encoder that can be configured to return specific values or errors.
type Option ¶
Option is a functional option that can be used to configure a Config instance.
func WithBinding ¶
WithBinding returns an Option that configures the Config instance to bind configuration data to a struct.
Example ¶
ExampleWithBinding demonstrates binding configuration to a struct.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
type ServerConfig struct {
Host string `config:"host"`
Port int `config:"port"`
}
type Config struct {
Server ServerConfig `config:"server"`
}
yamlContent := []byte(`
server:
host: localhost
port: 8080
`)
var appConfig Config
cfg, err := config.New(
config.WithContent(yamlContent, codec.TypeYAML),
config.WithBinding(&appConfig),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Printf("%s:%d\n", appConfig.Server.Host, appConfig.Server.Port)
}
Output: localhost:8080
func WithConsul ¶
WithConsul returns an Option that configures the Config instance to load configuration data from a Consul server. The format is automatically detected from the path extension. For custom formats, use WithConsulAs instead.
If CONSUL_HTTP_ADDR is not set, this option is silently skipped, allowing development without Consul while requiring it in production environments.
Paths support environment variable expansion using ${VAR} or $VAR syntax. Example: "${APP_ENV}/service.yaml" expands to "production/service.yaml" when APP_ENV=production
Required environment variables (production only):
- CONSUL_HTTP_ADDR: The address of the Consul server (e.g., "http://localhost:8500")
- CONSUL_HTTP_TOKEN: The access token for authentication with Consul (optional)
Example:
cfg := config.MustNew(
config.WithConsul("production/service.yaml"), // Auto-detects YAML, skipped without CONSUL_HTTP_ADDR
)
func WithConsulAs ¶
WithConsulAs returns an Option that configures the Config instance to load configuration data from a Consul server with explicit format. Use this when you need to override the format detection.
If CONSUL_HTTP_ADDR is not set, this option is silently skipped, allowing development without Consul while requiring it in production environments.
Paths support environment variable expansion using ${VAR} or $VAR syntax. Example: "${APP_ENV}/service" expands to "production/service" when APP_ENV=production
Required environment variables (production only):
- CONSUL_HTTP_ADDR: The address of the Consul server (e.g., "http://localhost:8500")
- CONSUL_HTTP_TOKEN: The access token for authentication with Consul (optional)
Example:
cfg := config.MustNew(
config.WithConsulAs("production/service", codec.TypeJSON),
)
func WithContent ¶
WithContent returns an Option that configures the Config instance to load configuration data from a byte slice. The codecType parameter specifies the format of the data (e.g., codec.TypeJSON, codec.TypeYAML).
Example:
yamlContent := []byte("server:\n port: 8080")
cfg := config.MustNew(
config.WithContent(yamlContent, codec.TypeYAML),
)
Example ¶
ExampleWithContent demonstrates loading configuration from byte content.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
jsonContent := []byte(`{
"app": {
"name": "MyApp",
"version": "1.0.0"
}
}`)
cfg, err := config.New(
config.WithContent(jsonContent, codec.TypeJSON),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("app.name"))
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("app.version"))
}
Output: MyApp 1.0.0
func WithDumper ¶
WithDumper adds a dumper to the configuration loader.
func WithEnv ¶
WithEnv returns an Option that configures the Config instance to load configuration data from environment variables. The prefix parameter specifies the prefix for the environment variables to be loaded. Environment variables are converted to lowercase and underscores create nested structures.
Example:
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithEnv("APP_"), // Loads APP_SERVER_PORT as server.port
)
func WithFile ¶
WithFile returns an Option that configures the Config instance to load configuration data from a file. The format is automatically detected from the file extension (.yaml, .yml, .json, .toml). For files without extensions or custom formats, use WithFileAs instead.
Paths support environment variable expansion using ${VAR} or $VAR syntax. Example: "${CONFIG_DIR}/app.yaml" expands to "/etc/myapp/app.yaml" when CONFIG_DIR=/etc/myapp
Example:
cfg := config.MustNew(
config.WithFile("config.yaml"), // Automatically detects YAML
config.WithFile("override.json"), // Automatically detects JSON
)
Example ¶
ExampleWithFile demonstrates loading configuration from a file.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
// Create a temporary config file (in real code, use an actual file path)
cfg, err := config.New(
config.WithContent([]byte(`{"name": "example"}`), codec.TypeJSON),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println(cfg.String("name"))
}
Output: example
func WithFileAs ¶
WithFileAs returns an Option that configures the Config instance to load configuration data from a file with explicit format. Use this when the file doesn't have an extension or when you need to override the format detection.
Paths support environment variable expansion using ${VAR} or $VAR syntax. Example: "${CONFIG_DIR}/app" expands to "/etc/myapp/app" when CONFIG_DIR=/etc/myapp
Example:
cfg := config.MustNew(
config.WithFileAs("config", codec.TypeYAML), // No extension, specify YAML
config.WithFileAs("config.dat", codec.TypeJSON), // Wrong extension, specify JSON
)
func WithFileDumper ¶
WithFileDumper returns an Option that configures the Config instance to dump configuration data to a file. The format is automatically detected from the file extension (.yaml, .yml, .json, .toml). For files without extensions or custom formats, use WithFileDumperAs instead.
Paths support environment variable expansion using ${VAR} or $VAR syntax. Example: "${LOG_DIR}/config.yaml" expands to "/var/log/config.yaml" when LOG_DIR=/var/log
Example:
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithFileDumper("output.yaml"), // Auto-detects YAML
)
func WithFileDumperAs ¶
WithFileDumperAs returns an Option that configures the Config instance to dump configuration data to a file with explicit format. Use this when the file doesn't have an extension or when you need to override the format detection.
Paths support environment variable expansion using ${VAR} or $VAR syntax. Example: "${OUTPUT_DIR}/config" expands to "/tmp/config" when OUTPUT_DIR=/tmp
Example:
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithFileDumperAs("output", codec.TypeYAML), // No extension, specify YAML
)
func WithJSONSchema ¶
WithJSONSchema adds a JSON Schema for validation.
func WithSource ¶
WithSource adds a source to the configuration loader.
func WithTag ¶
WithTag sets a custom struct tag name for binding (default: "config"). This allows you to use a different tag name if "config" conflicts with other libraries.
Example:
type Config struct {
Port int `cfg:"port"` // Using custom tag
}
cfg := config.MustNew(
config.WithFile("config.yaml"),
config.WithBinding(&appConfig),
config.WithTag("cfg"), // Use "cfg" instead of "config"
)
func WithValidator ¶
WithValidator adds a custom validation function.
Example ¶
ExampleWithValidator demonstrates using a custom validator.
package main
import (
"context"
"fmt"
"log"
"rivaas.dev/config"
"rivaas.dev/config/codec"
)
func main() {
yamlContent := []byte(`name: myapp`)
cfg, err := config.New(
config.WithContent(yamlContent, codec.TypeYAML),
config.WithValidator(func(cfgMap map[string]any) error {
// Custom validation logic
if _, ok := cfgMap["name"]; !ok {
return fmt.Errorf("name is required")
}
return nil
}),
)
if err != nil {
log.Fatal(err)
}
if err = cfg.Load(context.Background()); err != nil {
log.Fatal(err)
}
//nolint:errcheck // Error checking is omitted in the example test code
fmt.Println("Validation passed")
}
Output: Validation passed
type Source ¶
type Source interface {
// Load loads configuration data from the source.
// It returns a map containing the configuration key-value pairs.
// Keys are normalized to lowercase for case-insensitive access.
Load(ctx context.Context) (map[string]any, error)
}
Source defines the interface for configuration sources. Implementations load configuration data from various locations such as files, environment variables, or remote services.
Load must be safe to call concurrently.
func TestSource ¶
TestSource creates a mock source for testing with the given configuration map.
func TestSourceWithError ¶
TestSourceWithError creates a mock source that returns an error on Load.
type Validator ¶
type Validator interface {
Validate() error
}
Validator is an interface for structs that can validate their own configuration.
type Watcher ¶
type Watcher interface {
// Watch starts watching for changes to configuration data.
// It blocks until the context is cancelled or an error occurs.
Watch(ctx context.Context) error
}
Watcher defines the interface for watching configuration changes. Implementations monitor configuration sources for changes and notify when updates occur.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package codec provides functionality for encoding and decoding data.
|
Package codec provides functionality for encoding and decoding data. |
|
Package dumper provides configuration dumper implementations.
|
Package dumper provides configuration dumper implementations. |
|
examples
|
|
|
basic
command
|
|
|
comprehensive
command
|
|
|
environment
command
|
|
|
mixed
command
|
|
|
Package source provides configuration source implementations.
|
Package source provides configuration source implementations. |