Documentation
¶
Overview ¶
Package confkit provides a minimalist configuration management library with transparent layered configuration sources.
Key Features:
- Layered configuration with clear precedence (later layers win)
- Flexible key matching through normalization
- Full transparency - see which layer provided each value
- Simple string-based storage for easy debugging
Key Normalization:
Confkit automatically normalizes keys when matching, making these equivalent:
- "database.host" (dots - common in JSON/YAML, when flattened)
- "database_host" (underscores - common in env vars)
- "database-host" (hyphens - common in CLI flags)
- "DATABASE_HOST" (uppercase - typical for env vars)
This allows configuration from different sources (files, environment, flags) to work together seamlessly without manual key transformation.
Struct Unmarshaling:
When using Unmarshal with structs, fields are mapped using mapstructure tags:
type Config struct {
Database struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
} `mapstructure:"database"`
}
This creates the configuration paths "database.host" and "database.port", which can be set by any normalized variant (DATABASE_HOST, database-host, etc).
Handling Defaults (RECOMMENDED PATTERN):
Instead of using struct tags for defaults, create a DefaultConfig function:
func DefaultConfig() *Config {
return &Config{
Server: ServerConfig{
Host: "localhost",
Port: 8080,
},
Database: DatabaseConfig{
Port: 5432,
},
}
}
cfg := confkit.NewConfig()
cfg.Add(confkit.FromDefaults("defaults", DefaultConfig()))
cfg.Add(confkit.FromEnv("env", "APP_"))
This approach keeps defaults in code (not in tags), makes them testable, and allows the same defaults to be used for documentation generation.
Configuration Validation:
Confkit provides helpers for validation without enforcing any validation rules:
// Detect unknown configuration keys
unmatched := confkit.UnmatchedKeys(cfg, &config)
if len(unmatched) > 0 {
log.Printf("Warning: unknown keys: %v", unmatched)
}
// Enforce custom validation rules
rules := []confkit.ValidationRule{
{Path: "database.password", Required: true, Sources: []string{"env"}},
{Path: "server.tls.cert", RequiredWith: []string{"server.tls.key"}},
}
if errs := confkit.ValidateRules(cfg, rules); len(errs) > 0 {
// Handle validation errors
}
This design keeps confkit minimal while enabling sophisticated validation patterns.
Index ¶
- func Flatten(m map[string]any) (map[string]string, error)
- func LayerUnmatchedKeys(layer *Layer, target any) []string
- func RemapKeys(values, aliases map[string]string) map[string]string
- func Transform(key string, transformer TransformFunc) string
- func TransformFlat(key string, transformer TransformFunc, prefix string) string
- func TransformSegments(key string, transformer TransformFunc, separator string) string
- func UnmatchedKeys(cfg *Config, target any) []string
- func ValidateRules(cfg *Config, rules []ValidationRule) []error
- type Config
- func (c *Config) Add(layer *Layer) *Layer
- func (c *Config) Get(key string) (value, sourceKey, sourceName string, found bool)
- func (c *Config) GetBool(key string) (bool, error)
- func (c *Config) GetDuration(key string) (time.Duration, error)
- func (c *Config) GetFloat(key string) (float64, error)
- func (c *Config) GetInt(key string) (int, error)
- func (c *Config) GetValue(key string) (string, bool)
- func (c *Config) Unmarshal(target any) error
- type Field
- type Layer
- type Node
- type TransformFunc
- type ValidationRule
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Flatten ¶
Flatten converts a hierarchical map[string]any to a flat map[string]string. Nested maps are flattened using dot notation. Arrays/slices are skipped as they don't make sense for configuration. Nil values are skipped (key not present in result). Empty strings are preserved as "" (different from missing).
func LayerUnmatchedKeys ¶
LayerUnmatchedKeys returns unmatched keys for a specific layer. This is useful for validating configuration from specific sources.
func RemapKeys ¶
RemapKeys transforms keys in a map according to the provided aliases. The aliases map contains oldKey → newKey mappings. If multiple old keys map to the same new key, the last one wins. Keys not found in aliases are passed through unchanged.
func Transform ¶
func Transform(key string, transformer TransformFunc) string
Transform applies a transformation function to a simple key.
func TransformFlat ¶
func TransformFlat(key string, transformer TransformFunc, prefix string) string
TransformFlat flattens dots to underscores and applies transformation. Useful for environment variables: "server.port" → "SERVER_PORT".
func TransformSegments ¶
func TransformSegments(key string, transformer TransformFunc, separator string) string
TransformSegments applies transformation to each dot-separated segment. Useful for CLI flags: "server.maxConns" → "server-max-conns" The separator parameter is used as a prefix and between segments.
func UnmatchedKeys ¶
UnmatchedKeys returns all configuration keys that don't match any field in the target struct. This is useful for detecting typos, deprecated keys, or keys from wrong configuration files.
Example:
unmatched := confkit.UnmatchedKeys(cfg, &config)
if len(unmatched) > 0 {
log.Printf("Warning: unknown configuration keys: %v", unmatched)
}
func ValidateRules ¶
func ValidateRules(cfg *Config, rules []ValidationRule) []error
ValidateRules checks configuration against custom validation rules. Returns a slice of validation errors. Empty slice means validation passed.
This is a helper for implementing custom validation logic. Confkit itself doesn't enforce validation - that's up to the application.
Example:
rules := []confkit.ValidationRule{
{Path: "database.password", Required: true, Sources: []string{"env"}},
{Path: "server.tls.cert", RequiredWith: []string{"server.tls.key"}},
}
if errs := confkit.ValidateRules(cfg, rules); len(errs) > 0 {
for _, err := range errs {
log.Printf("Validation error: %s", err)
}
os.Exit(1)
}
Types ¶
type Config ¶
type Config struct {
Layers []*Layer // Public for direct inspection
}
Config holds multiple configuration layers and provides resolution methods. Layers are evaluated in order, with later layers taking precedence.
func (*Config) Add ¶
Add appends a Layer to the configuration and returns the added layer. Later layers take precedence over earlier ones during resolution. The returned *Layer can be used to update values at runtime:
envLayer := cfg.Add(confkit.FromEnv("env", "APP_"))
// Later: envLayer.Values["NEW_KEY"] = "new_value"
func (*Config) Get ¶
Get resolves a configuration key across all layers.
Key matching uses normalization, making these formats equivalent:
- "database.host" (dots - common in JSON/YAML)
- "database_host" (underscores - common in env vars)
- "database-host" (hyphens - common in CLI flags)
- "DATABASE_HOST" (uppercase - typical for env vars)
- Any combination: "DATABASE.HOST", "Database-Host", etc.
Returns:
- value: The configuration value
- sourceKey: The original key as it appears in the source (before normalization)
- sourceName: The name of the layer containing this value
- found: Whether the key was found
Later layers take precedence over earlier ones.
func (*Config) GetBool ¶
GetBool retrieves a configuration value as a boolean. Returns an error if the key is not found or the value cannot be parsed as a boolean. Accepts: 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
func (*Config) GetDuration ¶
GetDuration retrieves a configuration value as a time.Duration. Returns an error if the key is not found or the value cannot be parsed as a duration. Accepts any valid duration string: "300ms", "1.5h", "2h45m".
func (*Config) GetFloat ¶
GetFloat retrieves a configuration value as a float64. Returns an error if the key is not found or the value cannot be parsed as a float.
func (*Config) GetInt ¶
GetInt retrieves a configuration value as an integer. Returns an error if the key is not found or the value cannot be parsed as an integer.
func (*Config) GetValue ¶
GetValue is a simplified version of Get that returns just the value and whether it was found. This is convenient for cases where you don't need source information.
func (*Config) Unmarshal ¶
Unmarshal populates the target struct with configuration values.
It uses the mapstructure library to decode configuration into the target struct. The mapping between configuration keys and struct fields works as follows:
1. Struct fields are identified by their mapstructure tags 2. Nested structs create dotted paths (e.g., "server.port") 3. Keys are matched using normalization - these are all equivalent:
- "server.port" (dots)
- "server_port" (underscores)
- "server-port" (hyphens)
- "SERVER_PORT" (uppercase)
- "SERVER.PORT" (uppercase with dots)
Example struct and matching configuration keys:
type Config struct {
Server struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
} `mapstructure:"server"`
}
// These configuration keys all map to Config.Server.Port:
cfg.Add(&Layer{
Name: "config",
Values: map[string]string{
"SERVER_PORT": "8080", // From environment
"server.port": "8080", // From JSON/YAML
"server-port": "8080", // From CLI flags
},
})
The target must be a pointer to a struct. Unmarshal returns an error if the target is not a pointer to a struct or if decoding fails.
type Field ¶
type Field struct {
Path string // Dot-separated path (e.g., "server.port")
Type string // Go type name (e.g., "int", "string")
Tags map[string]string // Tag values keyed by tag name
}
Field represents a configuration field extracted from a struct.
func ExtractFields ¶
ExtractFields returns a flattened list of fields from a struct with specified tags. The struct parameter should be a struct value or pointer to struct. Tag names specify which struct tags to extract (e.g., "mapstructure", "json", "description").
type Layer ¶
type Layer struct {
Name string // Source identifier (e.g., "env", "yaml:config.yaml", "sql:runtime")
Prefix string // Optional prefix to strip during matching (e.g., "MYAPP_")
Values map[string]string // Flat map with dot-separated keys
}
Layer represents a named configuration source with string key-value pairs. Keys are stored in their original format to support round-tripping.
func FromDefaults ¶
FromDefaults creates a configuration layer from a struct with default values. It uses reflection to extract non-zero values from the struct and creates a flat map of configuration values. This is the recommended way to handle defaults in confkit.
Example:
func DefaultConfig() *Config {
return &Config{
Server: ServerConfig{
Host: "localhost",
Port: 8080,
},
Database: DatabaseConfig{
Port: 5432,
MaxConnections: 100,
},
}
}
cfg := confkit.NewConfig()
cfg.Add(confkit.FromDefaults("defaults", DefaultConfig()))
type Node ¶
type Node struct {
Name string // Field or struct name
Type string // Go type (empty for non-leaf nodes)
Tags map[string]string // Tag values
Children []*Node // Child nodes (for structs)
}
Node represents a hierarchical structure element.
func ExtractStructure ¶
ExtractStructure returns a hierarchical representation of a struct with specified tags. The struct parameter should be a struct value or pointer to struct. Tag names specify which struct tags to extract.
type TransformFunc ¶
TransformFunc is a function that transforms a single string segment.
var ( // LowerCase converts to lowercase: "ServerPort" → "serverport". LowerCase TransformFunc = toLowerCaseASCII // UpperCase converts to uppercase: "serverPort" → "SERVERPORT". UpperCase TransformFunc = toUpperCaseASCII // SnakeCase converts to snake_case: "ServerPort" → "server_port". SnakeCase TransformFunc = toSnakeCase // UpperSnakeCase converts to UPPER_SNAKE_CASE: "serverPort" → "SERVER_PORT". UpperSnakeCase TransformFunc = toUpperSnakeCase // KebabCase converts to kebab-case: "serverPort" → "server-port". KebabCase TransformFunc = toKebabCase )
Standard transformers.
type ValidationRule ¶
type ValidationRule struct {
Path string // Field path (e.g., "database.password")
Required bool // Whether this field must be set
Sources []string // Allowed source layer names (empty = any source)
DisallowedSources []string // Disallowed source layer names
RequiredWith []string // Other fields that must be set if this is set
ConflictsWith []string // Fields that cannot be set if this is set
}
ValidationRule represents a custom validation rule for configuration fields.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
01-basic
command
|
|
|
02-environment
command
|
|
|
03-structs
command
|
|
|
04-typed-access
command
|
|
|
05-layering
command
|
|
|
06-struct-tags
command
|
|
|
07-env-generation
command
|
|
|
08-aliases
command
|
|
|
09-key-mapping
command
|
|
|
10-validation
command
|