1package db
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "log/slog"
8 "path/filepath"
9 "sync"
10
11 "github.com/pressly/goose/v3"
12)
13
14var gooseSetup sync.Once
15
16// Connect opens a SQLite database connection and runs migrations.
17func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
18 if dataDir == "" {
19 return nil, fmt.Errorf("data.dir is not set")
20 }
21 dbPath := filepath.Join(dataDir, "crush.db")
22
23 db, err := openDB(dbPath)
24 if err != nil {
25 return nil, err
26 }
27
28 if err = db.PingContext(ctx); err != nil {
29 db.Close()
30 return nil, fmt.Errorf("failed to connect to database: %w", err)
31 }
32
33 var gooseErr error
34 gooseSetup.Do(func() {
35 goose.SetBaseFS(FS)
36 if err := goose.SetDialect("sqlite3"); err != nil {
37 slog.Error("Failed to set dialect", "error", err)
38 gooseErr = fmt.Errorf("failed to set dialect: %w", err)
39 }
40 })
41 if gooseErr != nil {
42 return nil, gooseErr
43 }
44
45 if err := goose.Up(db, "migrations"); err != nil {
46 slog.Error("Failed to apply migrations", "error", err)
47 return nil, fmt.Errorf("failed to apply migrations: %w", err)
48 }
49
50 return db, nil
51}