from __future__ import annotations import sqlite3 from contextlib import contextmanager from datetime import datetime, timezone from pathlib import Path class BookmarkRepository: def __init__(self, db_path: str): self._db_path = db_path self._ensure_schema() def create_bookmark(self, path: str, label: str) -> dict: created_at = self._now_iso() with self._connection() as conn: cursor = conn.execute( """ INSERT INTO bookmarks (path, label, created_at) VALUES (?, ?, ?) """, (path, label, created_at), ) bookmark_id = int(cursor.lastrowid) row = conn.execute( "SELECT id, path, label, created_at FROM bookmarks WHERE id = ?", (bookmark_id,), ).fetchone() return self._to_dict(row) def list_bookmarks(self) -> list[dict]: with self._connection() as conn: rows = conn.execute( """ SELECT id, path, label, created_at FROM bookmarks ORDER BY created_at DESC """ ).fetchall() return [self._to_dict(row) for row in rows] def delete_bookmark(self, bookmark_id: int) -> bool: with self._connection() as conn: cursor = conn.execute("DELETE FROM bookmarks WHERE id = ?", (bookmark_id,)) return cursor.rowcount > 0 def _ensure_schema(self) -> None: db_path = Path(self._db_path) if db_path.parent and str(db_path.parent) not in {"", "."}: db_path.parent.mkdir(parents=True, exist_ok=True) with self._connection() as conn: conn.execute( """ CREATE TABLE IF NOT EXISTS bookmarks ( id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT NOT NULL UNIQUE, label TEXT NOT NULL, created_at TEXT NOT NULL ) """ ) conn.execute( """ CREATE INDEX IF NOT EXISTS idx_bookmarks_created_at_desc ON bookmarks(created_at DESC) """ ) @contextmanager def _connection(self): conn = sqlite3.connect(self._db_path) conn.row_factory = sqlite3.Row try: yield conn conn.commit() except Exception: conn.rollback() raise finally: conn.close() @staticmethod def _to_dict(row: sqlite3.Row) -> dict: return { "id": int(row["id"]), "path": row["path"], "label": row["label"], "created_at": row["created_at"], } @staticmethod def _now_iso() -> str: return datetime.now(tz=timezone.utc).isoformat().replace("+00:00", "Z")