codex-mobile-bridge 0.3.3

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

use rusqlite::{Connection, 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 display_name = display_name
            .map(str::trim)
            .filter(|value| !value.is_empty())
            .map(ToOwned::to_owned)
            .unwrap_or_else(|| default_display_name(&canonical));
        let inserted = insert_directory_bookmark_row(&conn, &path_string, &display_name, now, now)?;
        if inserted == 0 {
            update_directory_bookmark_row(&conn, &path_string, &display_name, now)?;
        }
        let created_at_ms = directory_bookmark_created_at(&conn, &path_string)?;

        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 inserted = insert_directory_history_row(&conn, &path_string, now)?;
        if inserted == 0 {
            increment_directory_history_row(&conn, &path_string, now)?;
        }
        let use_count = directory_history_use_count(&conn, &path_string)?;
        Ok(DirectoryHistoryRecord {
            path: normalized.to_string_lossy().to_string(),
            display_name: default_display_name(&normalized),
            last_used_at_ms: now,
            use_count,
        })
    }
}

fn update_directory_bookmark_row(
    conn: &Connection,
    path: &str,
    display_name: &str,
    updated_at_ms: i64,
) -> rusqlite::Result<()> {
    conn.execute(
        "UPDATE directory_bookmarks
         SET display_name = ?2, updated_at_ms = ?3
         WHERE path = ?1",
        params![path, display_name, updated_at_ms],
    )?;
    Ok(())
}

fn directory_bookmark_created_at(conn: &Connection, path: &str) -> rusqlite::Result<i64> {
    conn.query_row(
        "SELECT created_at_ms FROM directory_bookmarks WHERE path = ?1",
        params![path],
        |row| row.get(0),
    )
}

fn insert_directory_bookmark_row(
    conn: &Connection,
    path: &str,
    display_name: &str,
    created_at_ms: i64,
    updated_at_ms: i64,
) -> rusqlite::Result<usize> {
    conn.execute(
        "INSERT OR IGNORE INTO directory_bookmarks (path, display_name, created_at_ms, updated_at_ms)
         VALUES (?1, ?2, ?3, ?4)",
        params![path, display_name, created_at_ms, updated_at_ms],
    )
}

fn directory_history_use_count(conn: &Connection, path: &str) -> rusqlite::Result<i64> {
    conn.query_row(
        "SELECT use_count FROM directory_history WHERE path = ?1",
        params![path],
        |row| row.get(0),
    )
}

fn increment_directory_history_row(
    conn: &Connection,
    path: &str,
    last_used_at_ms: i64,
) -> rusqlite::Result<()> {
    conn.execute(
        "UPDATE directory_history
         SET last_used_at_ms = ?2, use_count = use_count + 1
         WHERE path = ?1",
        params![path, last_used_at_ms],
    )?;
    Ok(())
}

fn insert_directory_history_row(
    conn: &Connection,
    path: &str,
    last_used_at_ms: i64,
) -> rusqlite::Result<usize> {
    conn.execute(
        "INSERT OR IGNORE INTO directory_history (path, last_used_at_ms, use_count)
         VALUES (?1, ?2, 1)",
        params![path, last_used_at_ms],
    )
}