codex-mobile-bridge 0.2.10

Remote bridge and service manager for codex-mobile.
Documentation
use std::path::Path;

use rusqlite::{OptionalExtension, params};

use super::Storage;
use crate::bridge_protocol::{DirectoryBookmarkRecord, DirectoryHistoryRecord, now_millis};
use crate::directory::{
    canonicalize_directory, default_display_name, normalize_absolute_directory,
};

impl Storage {
    pub fn list_directory_bookmarks(&self) -> anyhow::Result<Vec<DirectoryBookmarkRecord>> {
        let conn = self.connect()?;
        let mut stmt = conn.prepare(
            "SELECT path, display_name, created_at_ms, updated_at_ms
             FROM directory_bookmarks
             ORDER BY display_name COLLATE NOCASE ASC",
        )?;

        let rows = stmt.query_map([], |row| {
            Ok(DirectoryBookmarkRecord {
                path: row.get(0)?,
                display_name: row.get(1)?,
                created_at_ms: row.get(2)?,
                updated_at_ms: row.get(3)?,
            })
        })?;

        Ok(rows.collect::<rusqlite::Result<Vec<_>>>()?)
    }

    pub fn upsert_directory_bookmark(
        &self,
        path: &Path,
        display_name: Option<&str>,
    ) -> anyhow::Result<DirectoryBookmarkRecord> {
        let canonical = canonicalize_directory(path)?;
        let path_string = canonical.to_string_lossy().to_string();
        let now = now_millis();
        let conn = self.connect()?;
        let existing = conn
            .query_row(
                "SELECT created_at_ms FROM directory_bookmarks WHERE path = ?1",
                params![path_string],
                |row| row.get::<_, i64>(0),
            )
            .optional()?;
        let created_at_ms = existing.unwrap_or(now);
        let display_name = display_name
            .map(str::trim)
            .filter(|value| !value.is_empty())
            .map(ToOwned::to_owned)
            .unwrap_or_else(|| default_display_name(&canonical));
        conn.execute(
            "INSERT INTO directory_bookmarks (path, display_name, created_at_ms, updated_at_ms)
             VALUES (?1, ?2, ?3, ?4)
             ON CONFLICT(path) DO UPDATE SET
                 display_name = excluded.display_name,
                 updated_at_ms = excluded.updated_at_ms",
            params![path_string, display_name, created_at_ms, now],
        )?;

        Ok(DirectoryBookmarkRecord {
            path: canonical.to_string_lossy().to_string(),
            display_name,
            created_at_ms,
            updated_at_ms: now,
        })
    }

    pub fn remove_directory_bookmark(&self, path: &Path) -> anyhow::Result<()> {
        let canonical = normalize_absolute_directory(path)?;
        let conn = self.connect()?;
        conn.execute(
            "DELETE FROM directory_bookmarks WHERE path = ?1",
            params![canonical.to_string_lossy().to_string()],
        )?;
        Ok(())
    }

    pub fn list_directory_history(
        &self,
        limit: usize,
    ) -> anyhow::Result<Vec<DirectoryHistoryRecord>> {
        let conn = self.connect()?;
        let mut stmt = conn.prepare(
            "SELECT path, last_used_at_ms, use_count
             FROM directory_history
             ORDER BY last_used_at_ms DESC
             LIMIT ?1",
        )?;
        let rows = stmt.query_map(params![limit.max(1) as i64], |row| {
            let path: String = row.get(0)?;
            Ok(DirectoryHistoryRecord {
                display_name: default_display_name(Path::new(&path)),
                path,
                last_used_at_ms: row.get(1)?,
                use_count: row.get(2)?,
            })
        })?;
        Ok(rows.collect::<rusqlite::Result<Vec<_>>>()?)
    }

    pub fn record_directory_usage(&self, path: &Path) -> anyhow::Result<DirectoryHistoryRecord> {
        let normalized = normalize_absolute_directory(path)?;
        let path_string = normalized.to_string_lossy().to_string();
        let now = now_millis();
        let conn = self.connect()?;
        let existing = conn
            .query_row(
                "SELECT use_count FROM directory_history WHERE path = ?1",
                params![&path_string],
                |row| row.get::<_, i64>(0),
            )
            .optional()?
            .unwrap_or(0);
        let use_count = existing + 1;
        conn.execute(
            "INSERT INTO directory_history (path, last_used_at_ms, use_count)
             VALUES (?1, ?2, ?3)
             ON CONFLICT(path) DO UPDATE SET
                 last_used_at_ms = excluded.last_used_at_ms,
                 use_count = directory_history.use_count + 1",
            params![path_string, now, use_count],
        )?;
        Ok(DirectoryHistoryRecord {
            path: normalized.to_string_lossy().to_string(),
            display_name: default_display_name(&normalized),
            last_used_at_ms: now,
            use_count,
        })
    }
}