108 lines
2.8 KiB
Go
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
|
|
}
|
|
}
|