Files
nexus/internal/preferences/preferences.go
T
2026-04-17 15:30:22 +01:00

108 lines
2.8 KiB
Go

// Package preferences manages application preferences with in-memory caching
// and persistence to a backing store.
package preferences
import (
"fmt"
"sync"
"github.com/andydixon/nexus/internal/models"
)
// PreferenceStore is the persistence interface used by the Manager.
type PreferenceStore interface {
GetPreferences() (*models.AppPreferences, error)
SavePreferences(prefs *models.AppPreferences) error
}
// Manager handles application preferences with defaults and caching.
// It is safe for concurrent use.
type Manager struct {
store PreferenceStore
cache *models.AppPreferences
mu sync.RWMutex
}
// NewManager creates a preferences Manager backed by the given store.
// Call Load to populate the cache from disc.
func NewManager(store PreferenceStore) *Manager {
return &Manager{store: store}
}
// Load reads preferences from the backing store into memory. If no
// preferences have been saved yet, sensible defaults are applied.
func (m *Manager) Load() error {
m.mu.Lock()
defer m.mu.Unlock()
prefs, err := m.store.GetPreferences()
if err != nil {
return fmt.Errorf("preferences: failed to load: %w", err)
}
if prefs == nil {
defaults := models.DefaultAppPreferences()
m.cache = &defaults
} else {
m.cache = prefs
m.applyDefaults()
}
return nil
}
// Get returns the current preferences. If Load has not been called, defaults
// are returned.
func (m *Manager) Get() *models.AppPreferences {
m.mu.RLock()
defer m.mu.RUnlock()
if m.cache == nil {
defaults := models.DefaultAppPreferences()
return &defaults
}
// Return a copy to prevent callers mutating the cache.
cp := *m.cache
return &cp
}
// Save persists the given preferences to the backing store and updates the
// in-memory cache.
func (m *Manager) Save(prefs *models.AppPreferences) error {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.store.SavePreferences(prefs); err != nil {
return fmt.Errorf("preferences: failed to save: %w", err)
}
cp := *prefs
m.cache = &cp
return nil
}
// applyDefaults fills in zero-valued fields from the default preferences.
// Caller must hold m.mu.
func (m *Manager) applyDefaults() {
defaults := models.DefaultAppPreferences()
if m.cache.Theme == "" {
m.cache.Theme = defaults.Theme
}
if m.cache.DefaultProtocol == "" {
m.cache.DefaultProtocol = defaults.DefaultProtocol
}
if m.cache.SidebarWidth == 0 {
m.cache.SidebarWidth = defaults.SidebarWidth
}
if m.cache.DefaultFontFamily == "" {
m.cache.DefaultFontFamily = defaults.DefaultFontFamily
}
if m.cache.DefaultFontSize == 0 {
m.cache.DefaultFontSize = defaults.DefaultFontSize
}
if m.cache.DefaultScrollback == 0 {
m.cache.DefaultScrollback = defaults.DefaultScrollback
}
if m.cache.DefaultCursorStyle == "" {
m.cache.DefaultCursorStyle = defaults.DefaultCursorStyle
}
}