Codex Handbook
ext/memories/src/local/list.rs 83 lines
use crate::MAX_LIST_RESULTS;use crate::backend::ListMemoriesRequest;use crate::backend::ListMemoriesResponse;use crate::backend::MemoriesBackendError;use crate::backend::MemoryEntry;use crate::backend::MemoryEntryType;use super::LocalMemoriesBackend;use super::path::display_relative_path;use super::path::is_hidden_path;use super::path::read_sorted_dir_paths;use super::path::reject_symlink;pub(super) async fn list(    backend: &LocalMemoriesBackend,    request: ListMemoriesRequest,) -> Result<ListMemoriesResponse, MemoriesBackendError> {    let max_results = request.max_results.min(MAX_LIST_RESULTS);    let start = backend.resolve_scoped_path(request.path.as_deref()).await?;    let start_index = match request.cursor.as_deref() {        Some(cursor) => cursor.parse::<usize>().map_err(|_| {            MemoriesBackendError::invalid_cursor(cursor, "must be a non-negative integer")        })?,        None => 0,    };    let Some(metadata) = LocalMemoriesBackend::metadata_or_none(&start).await? else {        return Err(MemoriesBackendError::NotFound {            path: request.path.unwrap_or_default(),        });    };    reject_symlink(&display_relative_path(&backend.root, &start), &metadata)?;    let mut entries = if metadata.is_file() {        vec![MemoryEntry {            path: display_relative_path(&backend.root, &start),            entry_type: MemoryEntryType::File,        }]    } else if metadata.is_dir() {        let mut entries = Vec::new();        for path in read_sorted_dir_paths(&start).await? {            if is_hidden_path(&path) {                continue;            }            let Some(metadata) = LocalMemoriesBackend::metadata_or_none(&path).await? else {                continue;            };            if metadata.file_type().is_symlink() {                continue;            }            let entry_type = if metadata.is_dir() {                MemoryEntryType::Directory            } else if metadata.is_file() {                MemoryEntryType::File            } else {                continue;            };            entries.push(MemoryEntry {                path: display_relative_path(&backend.root, &path),                entry_type,            });        }        entries    } else {        Vec::new()    };    if start_index > entries.len() {        return Err(MemoriesBackendError::invalid_cursor(            start_index.to_string(),            "exceeds result count",        ));    }    let end_index = start_index.saturating_add(max_results).min(entries.len());    let next_cursor = (end_index < entries.len()).then(|| end_index.to_string());    let truncated = next_cursor.is_some();    Ok(ListMemoriesResponse {        path: request.path,        entries: entries.drain(start_index..end_index).collect(),        next_cursor,        truncated,    })}