e4.0.0 Refactored, file watcher and improved builder, doc update

This commit is contained in:
2025-07-17 03:44:08 -04:00
parent 16dc829fd5
commit 2934ea9548
25 changed files with 3567 additions and 1828 deletions

168
cmd/main.go Normal file
View File

@ -0,0 +1,168 @@
// FILE: example/watch_demo.go
package main
import (
"context"
"errors"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/lixenwraith/config"
)
// AppConfig represents our application configuration
type AppConfig struct {
Server struct {
Host string `toml:"host"`
Port int `toml:"port"`
} `toml:"server"`
Database struct {
URL string `toml:"url"`
MaxConns int `toml:"max_conns"`
IdleTimeout time.Duration `toml:"idle_timeout"`
} `toml:"database"`
Features struct {
RateLimit bool `toml:"rate_limit"`
Caching bool `toml:"caching"`
} `toml:"features"`
}
func main() {
// Create configuration with defaults
defaults := &AppConfig{}
defaults.Server.Host = "localhost"
defaults.Server.Port = 8080
defaults.Database.MaxConns = 10
defaults.Database.IdleTimeout = 30 * time.Second
// Build configuration
cfg, err := config.NewBuilder().
WithDefaults(defaults).
WithEnvPrefix("MYAPP_").
WithFile("config.toml").
Build()
if err != nil && !errors.Is(err, config.ErrConfigNotFound) {
log.Fatal("Failed to load config:", err)
}
// Enable auto-update with custom options
watchOpts := config.WatchOptions{
PollInterval: 500 * time.Millisecond, // Check twice per second
Debounce: 200 * time.Millisecond, // Quick response
MaxWatchers: 10,
ReloadTimeout: 2 * time.Second,
VerifyPermissions: true, // SECURITY: Detect permission changes
}
cfg.AutoUpdateWithOptions(watchOpts)
defer cfg.StopAutoUpdate()
// Start watching for changes
changes := cfg.Watch()
// Context for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Handle signals
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
// Log initial configuration
logConfig(cfg)
// Watch for changes
go func() {
for {
select {
case <-ctx.Done():
return
case path := <-changes:
handleConfigChange(cfg, path)
}
}
}()
// Main loop
log.Println("Watching for configuration changes. Edit config.toml to see updates.")
log.Println("Press Ctrl+C to exit.")
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for {
select {
case <-sigCh:
log.Println("Shutting down...")
return
case <-ticker.C:
// Periodic health check
var port int64
port, _ = cfg.Get("server.port")
log.Printf("Server still running on port %d", port)
}
}
}
func handleConfigChange(cfg *config.Config, path string) {
switch path {
case "__file_deleted__":
log.Println("⚠️ Config file was deleted!")
case "__permissions_changed__":
log.Println("⚠️ SECURITY: Config file permissions changed!")
case "__reload_error__":
log.Printf("❌ Failed to reload config: %s", path)
case "__reload_timeout__":
log.Println("⚠️ Config reload timed out")
default:
// Normal configuration change
value, _ := cfg.Get(path)
log.Printf("📝 Config changed: %s = %v", path, value)
// Handle specific changes
switch path {
case "server.port":
log.Println("Port changed - server restart required")
case "database.url":
log.Println("Database URL changed - reconnection required")
case "features.rate_limit":
if cfg.Bool("features.rate_limit") {
log.Println("Rate limiting enabled")
} else {
log.Println("Rate limiting disabled")
}
}
}
}
func logConfig(cfg *config.Config) {
log.Println("Current configuration:")
log.Printf(" Server: %s:%d", cfg.String("server.host"), cfg.Int("server.port"))
log.Printf(" Database: %s (max_conns=%d)",
cfg.String("database.url"),
cfg.Int("database.max_conns"))
log.Printf(" Features: rate_limit=%v, caching=%v",
cfg.Bool("features.rate_limit"),
cfg.Bool("features.caching"))
}
// Example config.toml file:
/*
[server]
host = "localhost"
port = 8080
[database]
url = "postgres://localhost/myapp"
max_conns = 25
idle_timeout = "30s"
[features]
rate_limit = true
caching = false
*/