Major changes: - Converted all cobra commands from global variables to CLI struct methods - Eliminated global logger variable in favor of dependency injection - Fixed all errcheck linter issues by properly handling errors - Fixed Makefile to check formatting instead of modifying files - Integrated smartconfig library for configuration management - Added CLAUDE.md with project-specific development guidelines Key improvements: - All commands (daemon, install, status, info) now use CLI struct methods - Logger is injected as dependency through fx providers - Proper error handling for all DrawText and file.Close() calls - Configuration loading now uses smartconfig with proper defaults - Fixed formatting check in Makefile (make test no longer modifies files) Technical details: - Created CLI struct with log field (renamed from logger per request) - All command constructors return *cobra.Command from CLI methods - Config package uses smartconfig.NewFromAppName() correctly - Fixed all critical errcheck issues throughout the codebase - Maintained backward compatibility with existing functionality All tests passing, code formatted, and ready for deployment.
119 lines
2.7 KiB
Go
119 lines
2.7 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/smartconfig"
|
|
)
|
|
|
|
// Config holds the application configuration
|
|
type Config struct {
|
|
FramebufferDevice string
|
|
RotationInterval string
|
|
UpdateInterval string
|
|
Screens []string
|
|
LogLevel string
|
|
Width int
|
|
Height int
|
|
|
|
// Parsed durations (not from config)
|
|
rotationDuration time.Duration
|
|
updateDuration time.Duration
|
|
}
|
|
|
|
// Load loads configuration from all available sources
|
|
func Load(ctx context.Context) (*Config, error) {
|
|
// Start with defaults
|
|
cfg := &Config{
|
|
FramebufferDevice: "/dev/fb0",
|
|
RotationInterval: "10s",
|
|
UpdateInterval: "1s",
|
|
Screens: []string{"overview", "top_cpu", "top_memory"},
|
|
LogLevel: "info",
|
|
Width: 1920,
|
|
Height: 1080,
|
|
}
|
|
|
|
// Try to load from the default location for hdmistat
|
|
sc, err := smartconfig.NewFromAppName("hdmistat")
|
|
if err == nil {
|
|
// Override defaults with config file values if they exist
|
|
if val, err := sc.GetString("framebuffer_device"); err == nil && val != "" {
|
|
cfg.FramebufferDevice = val
|
|
}
|
|
|
|
if val, err := sc.GetString("rotation_interval"); err == nil && val != "" {
|
|
cfg.RotationInterval = val
|
|
}
|
|
|
|
if val, err := sc.GetString("update_interval"); err == nil && val != "" {
|
|
cfg.UpdateInterval = val
|
|
}
|
|
|
|
if val, err := sc.GetString("log_level"); err == nil && val != "" {
|
|
cfg.LogLevel = val
|
|
}
|
|
|
|
if val, err := sc.GetInt("width"); err == nil && val > 0 {
|
|
cfg.Width = val
|
|
}
|
|
|
|
if val, err := sc.GetInt("height"); err == nil && val > 0 {
|
|
cfg.Height = val
|
|
}
|
|
|
|
// Load screens array
|
|
if screensRaw, exists := sc.Get("screens"); exists {
|
|
if screens, ok := screensRaw.([]interface{}); ok && len(screens) > 0 {
|
|
cfg.Screens = make([]string, 0, len(screens))
|
|
for _, s := range screens {
|
|
if str, ok := s.(string); ok {
|
|
cfg.Screens = append(cfg.Screens, str)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse durations
|
|
cfg.rotationDuration, err = time.ParseDuration(cfg.RotationInterval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cfg.updateDuration, err = time.ParseDuration(cfg.UpdateInterval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
// GetRotationDuration returns the parsed rotation interval
|
|
func (c *Config) GetRotationDuration() time.Duration {
|
|
return c.rotationDuration
|
|
}
|
|
|
|
// GetUpdateDuration returns the parsed update interval
|
|
func (c *Config) GetUpdateDuration() time.Duration {
|
|
return c.updateDuration
|
|
}
|
|
|
|
// GetLogLevel returns the slog level
|
|
func (c *Config) GetLogLevel() slog.Level {
|
|
switch c.LogLevel {
|
|
case "debug":
|
|
return slog.LevelDebug
|
|
case "info":
|
|
return slog.LevelInfo
|
|
case "warn":
|
|
return slog.LevelWarn
|
|
case "error":
|
|
return slog.LevelError
|
|
default:
|
|
return slog.LevelInfo
|
|
}
|
|
}
|