148 lines
3.2 KiB
Go
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
|
|
}
|