e4.0.0 Refactored, file watcher and improved builder, doc update
This commit is contained in:
310
doc/file.md
Normal file
310
doc/file.md
Normal file
@ -0,0 +1,310 @@
|
||||
# Configuration Files
|
||||
|
||||
The config package supports TOML configuration files with automatic loading, discovery, and atomic saving.
|
||||
|
||||
## TOML Format
|
||||
|
||||
TOML (Tom's Obvious, Minimal Language) is the supported configuration format:
|
||||
|
||||
```toml
|
||||
# Basic values
|
||||
host = "localhost"
|
||||
port = 8080
|
||||
debug = false
|
||||
|
||||
# Nested sections
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 9090
|
||||
timeout = "30s"
|
||||
|
||||
[database]
|
||||
url = "postgres://localhost/mydb"
|
||||
max_conns = 25
|
||||
timeout = "5s"
|
||||
|
||||
# Arrays
|
||||
[features]
|
||||
enabled = ["auth", "api", "metrics"]
|
||||
|
||||
# Inline tables
|
||||
tls = { enabled = true, cert = "/path/to/cert", key = "/path/to/key" }
|
||||
```
|
||||
|
||||
## Loading Configuration Files
|
||||
|
||||
### Basic Loading
|
||||
|
||||
```go
|
||||
cfg := config.New()
|
||||
cfg.RegisterStruct("", &Config{})
|
||||
|
||||
if err := cfg.LoadFile("config.toml"); err != nil {
|
||||
if errors.Is(err, config.ErrConfigNotFound) {
|
||||
log.Println("Config file not found, using defaults")
|
||||
} else {
|
||||
log.Fatal("Failed to load config:", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Builder
|
||||
|
||||
```go
|
||||
cfg, err := config.NewBuilder().
|
||||
WithDefaults(&Config{}).
|
||||
WithFile("/etc/myapp/config.toml").
|
||||
Build()
|
||||
```
|
||||
|
||||
### Multiple File Attempts
|
||||
|
||||
```go
|
||||
// Try multiple locations
|
||||
locations := []string{
|
||||
"./config.toml",
|
||||
"~/.config/myapp/config.toml",
|
||||
"/etc/myapp/config.toml",
|
||||
}
|
||||
|
||||
var cfg *config.Config
|
||||
var err error
|
||||
|
||||
for _, path := range locations {
|
||||
cfg, err = config.NewBuilder().
|
||||
WithDefaults(&Config{}).
|
||||
WithFile(path).
|
||||
Build()
|
||||
|
||||
if err == nil || !errors.Is(err, config.ErrConfigNotFound) {
|
||||
break
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Automatic File Discovery
|
||||
|
||||
Use file discovery to find configuration automatically:
|
||||
|
||||
```go
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithDefaults(&Config{}).
|
||||
WithFileDiscovery(config.FileDiscoveryOptions{
|
||||
Name: "myapp",
|
||||
Extensions: []string{".toml", ".conf"},
|
||||
EnvVar: "MYAPP_CONFIG",
|
||||
CLIFlag: "--config",
|
||||
UseXDG: true,
|
||||
UseCurrentDir: true,
|
||||
Paths: []string{"/opt/myapp"},
|
||||
}).
|
||||
Build()
|
||||
```
|
||||
|
||||
Search order:
|
||||
1. CLI flag: `--config=/path/to/config.toml`
|
||||
2. Environment variable: `$MYAPP_CONFIG`
|
||||
3. Current directory: `./myapp.toml`, `./myapp.conf`
|
||||
4. XDG config: `~/.config/myapp/myapp.toml`
|
||||
5. System paths: `/etc/myapp/myapp.toml`
|
||||
6. Custom paths: `/opt/myapp/myapp.toml`
|
||||
|
||||
## Saving Configuration
|
||||
|
||||
### Save Current State
|
||||
|
||||
```go
|
||||
// Save all current values atomically
|
||||
if err := cfg.Save("config.toml"); err != nil {
|
||||
log.Fatal("Failed to save config:", err)
|
||||
}
|
||||
```
|
||||
|
||||
The save operation is atomic - it writes to a temporary file then renames it.
|
||||
|
||||
### Save Specific Source
|
||||
|
||||
```go
|
||||
// Save only values from environment variables
|
||||
if err := cfg.SaveSource("env-config.toml", config.SourceEnv); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Save only file-loaded values
|
||||
if err := cfg.SaveSource("file-only.toml", config.SourceFile); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
### Generate Default Configuration
|
||||
|
||||
```go
|
||||
// Create a default config file
|
||||
defaults := &Config{}
|
||||
// ... set default values ...
|
||||
|
||||
cfg, _ := config.NewBuilder().
|
||||
WithDefaults(defaults).
|
||||
Build()
|
||||
|
||||
// Save defaults as config template
|
||||
if err := cfg.SaveSource("config.toml.example", config.SourceDefault); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## File Structure Mapping
|
||||
|
||||
TOML structure maps directly to dot-notation paths:
|
||||
|
||||
```toml
|
||||
# Maps to "debug"
|
||||
debug = true
|
||||
|
||||
[server]
|
||||
# Maps to "server.host"
|
||||
host = "localhost"
|
||||
# Maps to "server.port"
|
||||
port = 8080
|
||||
|
||||
[server.tls]
|
||||
# Maps to "server.tls.enabled"
|
||||
enabled = true
|
||||
# Maps to "server.tls.cert"
|
||||
cert = "/path/to/cert"
|
||||
|
||||
[[users]]
|
||||
# Array elements: "users.0.name", "users.0.role"
|
||||
name = "admin"
|
||||
role = "administrator"
|
||||
|
||||
[[users]]
|
||||
# Array elements: "users.1.name", "users.1.role"
|
||||
name = "user"
|
||||
role = "standard"
|
||||
```
|
||||
|
||||
## Type Handling
|
||||
|
||||
TOML types map to Go types:
|
||||
|
||||
```toml
|
||||
# Strings
|
||||
name = "myapp"
|
||||
multiline = """
|
||||
Line one
|
||||
Line two
|
||||
"""
|
||||
|
||||
# Numbers
|
||||
port = 8080 # int64
|
||||
timeout = 30 # int64
|
||||
ratio = 0.95 # float64
|
||||
max_size = 1_000_000 # int64 (underscores allowed)
|
||||
|
||||
# Booleans
|
||||
enabled = true
|
||||
debug = false
|
||||
|
||||
# Dates/Times (RFC 3339)
|
||||
created_at = 2024-01-15T09:30:00Z
|
||||
expires = 2024-12-31
|
||||
|
||||
# Arrays
|
||||
ports = [8080, 8081, 8082]
|
||||
tags = ["production", "stable"]
|
||||
|
||||
# Tables (objects)
|
||||
[database]
|
||||
host = "localhost"
|
||||
port = 5432
|
||||
|
||||
# Array of tables
|
||||
[[servers]]
|
||||
name = "web1"
|
||||
host = "10.0.0.1"
|
||||
|
||||
[[servers]]
|
||||
name = "web2"
|
||||
host = "10.0.0.2"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
File loading can produce several error types:
|
||||
|
||||
```go
|
||||
err := cfg.LoadFile("config.toml")
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, config.ErrConfigNotFound):
|
||||
// File doesn't exist - often not fatal
|
||||
log.Println("No config file, using defaults")
|
||||
|
||||
case strings.Contains(err.Error(), "failed to parse TOML"):
|
||||
// TOML syntax error
|
||||
log.Fatal("Invalid TOML syntax:", err)
|
||||
|
||||
case strings.Contains(err.Error(), "failed to read"):
|
||||
// Permission or I/O error
|
||||
log.Fatal("Cannot read config file:", err)
|
||||
|
||||
default:
|
||||
log.Fatal("Config error:", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### File Permissions
|
||||
|
||||
```go
|
||||
// After saving, verify permissions
|
||||
info, err := os.Stat("config.toml")
|
||||
if err == nil {
|
||||
mode := info.Mode()
|
||||
if mode&0077 != 0 {
|
||||
log.Warn("Config file is world/group readable")
|
||||
// Fix permissions
|
||||
os.Chmod("config.toml", 0600)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Size Limits
|
||||
|
||||
Files and values have size limits:
|
||||
- Maximum file size: ~10MB (10 * MaxValueSize)
|
||||
- Maximum value size: 1MB
|
||||
|
||||
## Partial Loading
|
||||
|
||||
Load only specific sections:
|
||||
|
||||
```go
|
||||
var serverCfg ServerConfig
|
||||
if err := cfg.Scan("server", &serverCfg); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var dbCfg DatabaseConfig
|
||||
if err := cfg.Scan("database", &dbCfg); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Example Files**: Generate `.example` files with defaults
|
||||
2. **Check Permissions**: Ensure config files aren't world-readable
|
||||
3. **Validate After Load**: Add validators to check loaded values
|
||||
4. **Handle Missing Files**: Missing config files often aren't fatal
|
||||
5. **Use Atomic Saves**: The built-in Save method is atomic
|
||||
6. **Document Structure**: Comment your TOML files thoroughly
|
||||
|
||||
## See Also
|
||||
|
||||
- [Live Reconfiguration](reconfiguration.md) - Automatic file reloading
|
||||
- [Builder Pattern](builder.md) - File discovery options
|
||||
- [Access Patterns](access.md) - Working with loaded values
|
||||
Reference in New Issue
Block a user