// Package settings is a tiny key/value store backed by the SQLite settings table. package settings import ( "context" "database/sql" "fmt" ) type Store struct { db *sql.DB } func NewStore(db *sql.DB) *Store { return &Store{db: db} } // Get returns the value for key, or "" if not set. func (s *Store) Get(ctx context.Context, key string) (string, error) { var v string err := s.db.QueryRowContext(ctx, `SELECT value FROM settings WHERE key = ?`, key).Scan(&v) if err == sql.ErrNoRows { return "", nil } return v, err } // Set upserts a key/value pair. func (s *Store) Set(ctx context.Context, key, value string) error { _, err := s.db.ExecContext(ctx, ` INSERT INTO settings(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`, key, value) if err != nil { return fmt.Errorf("set %s: %w", key, err) } return nil } // All returns every stored setting. Used by the UI to populate the prefs panel. func (s *Store) All(ctx context.Context) (map[string]string, error) { rows, err := s.db.QueryContext(ctx, `SELECT key, value FROM settings`) if err != nil { return nil, err } defer rows.Close() out := map[string]string{} for rows.Next() { var k, v string if err := rows.Scan(&k, &v); err != nil { return nil, err } out[k] = v } return out, rows.Err() } // GetMany fetches several keys in a single round-trip. func (s *Store) GetMany(ctx context.Context, keys ...string) (map[string]string, error) { out := make(map[string]string, len(keys)) for _, k := range keys { v, err := s.Get(ctx, k) if err != nil { return nil, err } out[k] = v } return out, nil }