e4.0.0 Refactored, file watcher and improved builder, doc update
This commit is contained in:
295
doc/builder.md
Normal file
295
doc/builder.md
Normal file
@ -0,0 +1,295 @@
|
||||
# Builder Pattern
|
||||
|
||||
The builder pattern provides fine-grained control over configuration initialization and loading behavior.
|
||||
|
||||
## Basic Builder Usage
|
||||
|
||||
```go
|
||||
cfg, err := config.NewBuilder().
|
||||
WithDefaults(defaultStruct).
|
||||
WithEnvPrefix("MYAPP_").
|
||||
WithFile("config.toml").
|
||||
Build()
|
||||
```
|
||||
|
||||
## Builder Methods
|
||||
|
||||
### WithDefaults
|
||||
|
||||
Register a struct containing default values:
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
Host string `toml:"host"`
|
||||
Port int `toml:"port"`
|
||||
}
|
||||
|
||||
defaults := &Config{
|
||||
Host: "localhost",
|
||||
Port: 8080,
|
||||
}
|
||||
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithDefaults(defaults).
|
||||
Build()
|
||||
```
|
||||
|
||||
### WithTarget
|
||||
|
||||
Enable type-aware mode with automatic struct population:
|
||||
|
||||
```go
|
||||
var appConfig Config
|
||||
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithTarget(&appConfig). // Registers struct and enables AsStruct()
|
||||
WithFile("config.toml").
|
||||
Build()
|
||||
|
||||
// Access populated struct
|
||||
populated, _ := cfg.AsStruct()
|
||||
config := populated.(*Config)
|
||||
```
|
||||
|
||||
### WithTagName
|
||||
|
||||
Use different struct tags for field mapping:
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
Server struct {
|
||||
Host string `json:"host"` // Using JSON tags
|
||||
Port int `json:"port"`
|
||||
} `json:"server"`
|
||||
}
|
||||
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithDefaults(&Config{}).
|
||||
WithTagName("json"). // Use json tags instead of toml
|
||||
Build()
|
||||
```
|
||||
|
||||
Supported tag names: `toml` (default), `json`, `yaml`
|
||||
|
||||
### WithPrefix
|
||||
|
||||
Add a prefix to all registered paths:
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithDefaults(serverConfig).
|
||||
WithPrefix("server"). // All paths prefixed with "server."
|
||||
Build()
|
||||
|
||||
// Access as "server.host" instead of just "host"
|
||||
host, _ := cfg.Get("server.host")
|
||||
```
|
||||
|
||||
### WithEnvPrefix
|
||||
|
||||
Set environment variable prefix:
|
||||
|
||||
```go
|
||||
cfg, err := config.NewBuilder().
|
||||
WithEnvPrefix("MYAPP_").
|
||||
Build()
|
||||
|
||||
// Reads from MYAPP_SERVER_PORT for "server.port"
|
||||
```
|
||||
|
||||
### WithSources
|
||||
|
||||
Configure source precedence order:
|
||||
|
||||
```go
|
||||
// Environment variables take highest priority
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithSources(
|
||||
config.SourceEnv,
|
||||
config.SourceFile,
|
||||
config.SourceCLI,
|
||||
config.SourceDefault,
|
||||
).
|
||||
Build()
|
||||
```
|
||||
|
||||
### WithEnvTransform
|
||||
|
||||
Custom environment variable name mapping:
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithEnvTransform(func(path string) string {
|
||||
// Custom mapping logic
|
||||
switch path {
|
||||
case "server.port":
|
||||
return "PORT" // Use $PORT instead of $MYAPP_SERVER_PORT
|
||||
case "database.url":
|
||||
return "DATABASE_URL"
|
||||
default:
|
||||
// Default transformation
|
||||
return "MYAPP_" + strings.ToUpper(
|
||||
strings.ReplaceAll(path, ".", "_"),
|
||||
)
|
||||
}
|
||||
}).
|
||||
Build()
|
||||
```
|
||||
|
||||
### WithEnvWhitelist
|
||||
|
||||
Limit which configuration paths check environment variables:
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithEnvWhitelist(
|
||||
"server.port",
|
||||
"database.url",
|
||||
"api.key",
|
||||
). // Only these paths read from env
|
||||
Build()
|
||||
```
|
||||
|
||||
### WithValidator
|
||||
|
||||
Add validation functions that run after loading:
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithDefaults(defaults).
|
||||
WithValidator(func(c *config.Config) error {
|
||||
// Validate port range
|
||||
port, _ := c.Get("server.port")
|
||||
if p := port.(int64); p < 1024 || p > 65535 {
|
||||
return fmt.Errorf("port must be between 1024-65535")
|
||||
}
|
||||
return nil
|
||||
}).
|
||||
WithValidator(func(c *config.Config) error {
|
||||
// Validate required fields
|
||||
return c.Validate("api.key", "database.url")
|
||||
}).
|
||||
Build()
|
||||
```
|
||||
|
||||
### WithFile
|
||||
|
||||
Set configuration file path:
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithFile("/etc/myapp/config.toml").
|
||||
Build()
|
||||
```
|
||||
|
||||
### WithArgs
|
||||
|
||||
Override command-line arguments (default is os.Args[1:]):
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithArgs([]string{"--debug", "--server.port=9090"}).
|
||||
Build()
|
||||
```
|
||||
|
||||
### WithFileDiscovery
|
||||
|
||||
Enable automatic configuration file discovery:
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithFileDiscovery(config.FileDiscoveryOptions{
|
||||
Name: "myapp",
|
||||
Extensions: []string{".toml", ".conf"},
|
||||
EnvVar: "MYAPP_CONFIG",
|
||||
CLIFlag: "--config",
|
||||
UseXDG: true,
|
||||
}).
|
||||
Build()
|
||||
```
|
||||
|
||||
This searches for configuration files in:
|
||||
1. Path specified by `--config` flag
|
||||
2. Path in `$MYAPP_CONFIG` environment variable
|
||||
3. Current directory
|
||||
4. XDG config directories (`~/.config/myapp/`, `/etc/myapp/`)
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Type-Safe Configuration Access
|
||||
|
||||
```go
|
||||
type AppConfig struct {
|
||||
Server ServerConfig `toml:"server"`
|
||||
DB DBConfig `toml:"database"`
|
||||
}
|
||||
|
||||
var conf AppConfig
|
||||
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithTarget(&conf).
|
||||
WithFile("config.toml").
|
||||
Build()
|
||||
|
||||
// Direct struct access after building
|
||||
fmt.Printf("Port: %d\n", conf.Server.Port)
|
||||
|
||||
// Or get updated struct anytime
|
||||
latest, _ := cfg.AsStruct()
|
||||
appConf := latest.(*AppConfig)
|
||||
```
|
||||
|
||||
### Multi-Stage Validation
|
||||
|
||||
```go
|
||||
cfg, err := config.NewBuilder().
|
||||
WithDefaults(defaults).
|
||||
// Stage 1: Validate structure
|
||||
WithValidator(validateStructure).
|
||||
// Stage 2: Validate values
|
||||
WithValidator(validateRanges).
|
||||
// Stage 3: Validate relationships
|
||||
WithValidator(validateRelationships).
|
||||
Build()
|
||||
|
||||
func validateStructure(c *config.Config) error {
|
||||
required := []string{"server.host", "server.port", "database.url"}
|
||||
return c.Validate(required...)
|
||||
}
|
||||
|
||||
func validateRanges(c *config.Config) error {
|
||||
port, _ := c.Get("server.port")
|
||||
if p := port.(int64); p < 1 || p > 65535 {
|
||||
return fmt.Errorf("invalid port: %d", p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateRelationships(c *config.Config) error {
|
||||
// Validate that related values make sense together
|
||||
// e.g., if SSL is enabled, ensure cert paths are set
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
The builder accumulates errors and returns them on `Build()`:
|
||||
|
||||
```go
|
||||
cfg, err := config.NewBuilder().
|
||||
WithTarget(nil). // Error: nil target
|
||||
WithTagName("invalid"). // Error: unsupported tag
|
||||
Build()
|
||||
|
||||
if err != nil {
|
||||
// err contains first error encountered
|
||||
}
|
||||
```
|
||||
|
||||
For panic on error use `MustBuild()`
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](env.md) - Environment configuration details
|
||||
- [Live Reconfiguration](reconfiguration.md) - File watching with builder
|
||||
Reference in New Issue
Block a user