// Package store owns the encrypted SQLite config and read state. package store import ( "database/sql" "fmt" "os" "path/filepath" "runtime" "strconv" _ "modernc.org/sqlite" ) // Store wraps the database and the field-encryption key. type Store struct { db *sql.DB key []byte } // Open opens (creating if needed) the DB at path and applies the schema. func Open(path string, key []byte) (*Store, error) { if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { return nil, fmt.Errorf("create db dir: %w", err) } db, err := sql.Open("sqlite", path) if err != nil { return nil, err } if _, err := db.Exec("PRAGMA foreign_keys = ON;"); err != nil { db.Close() return nil, err } if _, err := db.Exec(schemaSQL); err != nil { db.Close() return nil, fmt.Errorf("apply schema: %w", err) } s := &Store{db: db, key: key} if _, err := s.GetSetting("schema_version"); err != nil { if err := s.SetSetting("schema_version", strconv.Itoa(schemaVersion)); err != nil { db.Close() return nil, err } } return s, nil } func (s *Store) Close() error { return s.db.Close() } // DefaultDBPath resolves EMCLI_DB or the per-OS default location. func DefaultDBPath() (string, error) { if p := os.Getenv("EMCLI_DB"); p != "" { return p, nil } if runtime.GOOS == "windows" { if dir := os.Getenv("AppData"); dir != "" { return filepath.Join(dir, "emcli", "emcli.db"), nil } } home, err := os.UserHomeDir() if err != nil { return "", err } return filepath.Join(home, ".config", "emcli", "emcli.db"), nil }