cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package store
2
3import (
4 "database/sql"
5 "embed"
6 "fmt"
7 "os"
8 "path/filepath"
9 "runtime"
10
11 _ "github.com/mattn/go-sqlite3"
12)
13
14//go:embed sql/migrations
15var migrationFiles embed.FS
16
17// Database wraps sql.DB with application-specific methods
18type Database struct {
19 *sql.DB
20 path string
21}
22
23// GetConfigDir returns the appropriate configuration directory based on the OS
24var GetConfigDir = func() (string, error) {
25 var configDir string
26
27 switch runtime.GOOS {
28 case "windows":
29 appData := os.Getenv("APPDATA")
30 if appData == "" {
31 return "", fmt.Errorf("APPDATA environment variable not set")
32 }
33 configDir = filepath.Join(appData, "noteleaf")
34 default:
35 xdgConfigHome := os.Getenv("XDG_CONFIG_HOME")
36 if xdgConfigHome == "" {
37 homeDir, err := os.UserHomeDir()
38 if err != nil {
39 return "", fmt.Errorf("failed to get user home directory: %w", err)
40 }
41 xdgConfigHome = filepath.Join(homeDir, ".config")
42 }
43 configDir = filepath.Join(xdgConfigHome, "noteleaf")
44 }
45
46 // Create the directory if it doesn't exist
47 if err := os.MkdirAll(configDir, 0755); err != nil {
48 return "", fmt.Errorf("failed to create config directory: %w", err)
49 }
50
51 return configDir, nil
52}
53
54// NewDatabase creates and initializes a new database connection
55func NewDatabase() (*Database, error) {
56 configDir, err := GetConfigDir()
57 if err != nil {
58 return nil, fmt.Errorf("failed to get config directory: %w", err)
59 }
60
61 dbPath := filepath.Join(configDir, "noteleaf.db")
62
63 db, err := sql.Open("sqlite3", dbPath)
64 if err != nil {
65 return nil, fmt.Errorf("failed to open database: %w", err)
66 }
67
68 if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil {
69 db.Close()
70 return nil, fmt.Errorf("failed to enable foreign keys: %w", err)
71 }
72
73 if _, err := db.Exec("PRAGMA journal_mode = WAL"); err != nil {
74 db.Close()
75 return nil, fmt.Errorf("failed to enable WAL mode: %w", err)
76 }
77
78 database := &Database{
79 DB: db,
80 path: dbPath,
81 }
82
83 runner := NewMigrationRunner(db, migrationFiles)
84 if err := runner.RunMigrations(); err != nil {
85 db.Close()
86 return nil, fmt.Errorf("failed to run migrations: %w", err)
87 }
88
89 return database, nil
90}
91
92// NewMigrationRunnerFromDB creates a new migration runner from a Database instance
93func (db *Database) NewMigrationRunner() *MigrationRunner {
94 return NewMigrationRunner(db.DB, migrationFiles)
95}
96
97// GetPath returns the database file path
98func (db *Database) GetPath() string {
99 return db.path
100}
101
102// Close closes the database connection
103func (db *Database) Close() error {
104 return db.DB.Close()
105}