Files
photocli/internal/manifest/sqlite.go
T
Ein Anderssono 7555b561bd
pipeline / build (push) Has been cancelled
pipeline / test (push) Has been cancelled
v0.9.0: add manifest checksums
2026-06-15 02:19:47 +02:00

148 lines
3.2 KiB
Go

package manifest
import (
"database/sql"
"fmt"
"os"
"path/filepath"
_ "modernc.org/sqlite"
)
type sqliteManifest struct {
path string
db *sql.DB
open sqlOpenerFunc
execFunc func(query string, args ...any) (sql.Result, error)
}
type sqlOpenerFunc func(driverName, dataSourceName string) (*sql.DB, error)
var sqliteOpenFunc sqlOpenerFunc
func defaultSQLOpener() sqlOpenerFunc {
if sqliteOpenFunc != nil {
return sqliteOpenFunc
}
return sql.Open
}
func SQLitePath(dir string) string {
return filepath.Join(dir, "downloads.db")
}
func LoadSQLite(dir string) (*sqliteManifest, error) {
m := &sqliteManifest{
path: SQLitePath(dir),
open: defaultSQLOpener(),
}
return m, nil
}
func (m *sqliteManifest) OpenAppend() error {
if m.db != nil {
return nil
}
if err := os.MkdirAll(filepath.Dir(m.path), 0755); err != nil {
return err
}
opener := m.open
if opener == nil {
opener = sql.Open
}
db, err := opener("sqlite", m.path)
if err != nil {
return fmt.Errorf("open sqlite: %w", err)
}
execFn := m.execFunc
if execFn == nil {
execFn = db.Exec
}
_, err = execFn(`CREATE TABLE IF NOT EXISTS downloads (
id TEXT PRIMARY KEY,
filename TEXT NOT NULL DEFAULT '',
path TEXT NOT NULL DEFAULT '',
size INTEGER NOT NULL DEFAULT 0,
cloud TEXT NOT NULL DEFAULT '',
checksum TEXT NOT NULL DEFAULT '',
exported INTEGER NOT NULL DEFAULT 0
)`)
if err != nil {
db.Close()
return fmt.Errorf("create table: %w", err)
}
_, _ = execFn(`ALTER TABLE downloads ADD COLUMN path TEXT NOT NULL DEFAULT ''`)
_, _ = execFn(`ALTER TABLE downloads ADD COLUMN checksum TEXT NOT NULL DEFAULT ''`)
_, err = execFn(`CREATE INDEX IF NOT EXISTS idx_downloads_id ON downloads(id)`)
if err != nil {
db.Close()
return fmt.Errorf("create index: %w", err)
}
m.db = db
return nil
}
func (m *sqliteManifest) Has(id string) bool {
if m.db == nil {
return false
}
var count int
err := m.db.QueryRow(`SELECT COUNT(*) FROM downloads WHERE id = ?`, id).Scan(&count)
if err != nil {
return false
}
return count > 0
}
func (m *sqliteManifest) Add(id string, filename string, size int64, cloud string) {
m.AddEntry(newEntry(id, filename, size, cloud))
}
func (m *sqliteManifest) AddEntry(entry Entry) {
if m.db == nil {
return
}
if entry.Path == "" {
entry.Path = entry.Filename
}
m.db.Exec(`INSERT OR REPLACE INTO downloads (id, filename, path, size, cloud, checksum, exported) VALUES (?, ?, ?, ?, ?, ?, ?)`,
entry.ID, entry.Filename, entry.Path, entry.Size, entry.Cloud, entry.Checksum, entry.Exported)
}
func (m *sqliteManifest) Save() error {
return nil
}
func (m *sqliteManifest) Close() {
if m.db != nil {
m.db.Close()
m.db = nil
}
}
func (m *sqliteManifest) DB() *sql.DB {
return m.db
}
func (m *sqliteManifest) Entries() map[string]Entry {
if m.db == nil {
return nil
}
out := make(map[string]Entry)
rows, err := m.db.Query(`SELECT id, filename, path, size, cloud, checksum, exported FROM downloads`)
if err != nil {
return out
}
defer rows.Close()
for rows.Next() {
var e Entry
if err := rows.Scan(&e.ID, &e.Filename, &e.Path, &e.Size, &e.Cloud, &e.Checksum, &e.Exported); err == nil {
if e.Path == "" {
e.Path = e.Filename
}
out[e.ID] = e
}
}
return out
}