1package db
2
3import (
4 "database/sql"
5 "fmt"
6 "log"
7 "os"
8 "path/filepath"
9
10 "github.com/bnema/dumber/internal/config"
11 "github.com/bnema/dumber/internal/migrations"
12 _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver
13 _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite
14)
15
16// InitDB initializes the database connection and schema
17func InitDB(dbPath string) (*sql.DB, error) {
18 if dbPath == "" {
19 return nil, fmt.Errorf("database path cannot be empty")
20 }
21
22 // Ensure database directory exists
23 if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
24 return nil, fmt.Errorf("failed to create database directory: %w", err)
25 }
26
27 // Open database connection
28 database, err := sql.Open("sqlite3", dbPath)
29 if err != nil {
30 return nil, fmt.Errorf("failed to open database: %w", err)
31 }
32
33 // Test the connection
34 if err := database.Ping(); err != nil {
35 if err := database.Close(); err != nil {
36 log.Printf("Warning: failed to close database: %v", err)
37 }
38 return nil, fmt.Errorf("failed to connect to database: %w", err)
39 }
40
41 // Run embedded migrations - single source of truth for schema initialization
42 if err := migrations.RunEmbeddedMigrations(database); err != nil {
43 if err := database.Close(); err != nil {
44 log.Printf("Warning: failed to close database: %v", err)
45 }
46 return nil, fmt.Errorf("failed to run database migrations: %w", err)
47 }
48
49 // Verify all migrations are applied
50 if err := migrations.VerifyAllMigrationsApplied(database); err != nil {
51 if err := database.Close(); err != nil {
52 log.Printf("Warning: failed to close database: %v", err)
53 }
54 return nil, fmt.Errorf("migration verification failed: %w", err)
55 }
56
57 return database, nil
58}
59
60// InitDBWithConfig initializes the database with configuration-based shortcuts
61func InitDBWithConfig(dbPath string, cfg *config.Config) (*sql.DB, error) {
62 db, err := InitDB(dbPath)
63 if err != nil {
64 return nil, err
65 }
66
67 // Initialize shortcuts from configuration
68 if err := initializeShortcuts(db, cfg); err != nil {
69 if err := db.Close(); err != nil {
70 log.Printf("Warning: failed to close database: %v", err)
71 }
72 return nil, fmt.Errorf("failed to initialize shortcuts: %w", err)
73 }
74
75 return db, nil
76}
77
78// initializeShortcuts inserts configured shortcuts into the database
79func initializeShortcuts(db *sql.DB, cfg *config.Config) error {
80 if cfg == nil || cfg.SearchShortcuts == nil {
81 return nil // No shortcuts to initialize
82 }
83
84 // Insert configured shortcuts
85 for shortcut, shortcutCfg := range cfg.SearchShortcuts {
86 _, err := db.Exec(
87 "INSERT OR IGNORE INTO shortcuts (shortcut, url_template, description) VALUES (?, ?, ?)",
88 shortcut, shortcutCfg.URL, shortcutCfg.Description,
89 )
90 if err != nil {
91 return fmt.Errorf("failed to insert shortcut %s: %w", shortcut, err)
92 }
93 }
94
95 return nil
96}