Codex Handbook
memories/read/src/citations.rs 85 lines
use codex_protocol::ThreadId;use codex_protocol::memory_citation::MemoryCitation;use codex_protocol::memory_citation::MemoryCitationEntry;use std::collections::HashSet;pub fn parse_memory_citation(citations: Vec<String>) -> Option<MemoryCitation> {    let mut entries = Vec::new();    let mut rollout_ids = Vec::new();    let mut seen_rollout_ids = HashSet::new();    for citation in citations {        if let Some(entries_block) =            extract_block(&citation, "<citation_entries>", "</citation_entries>")        {            entries.extend(                entries_block                    .lines()                    .filter_map(parse_memory_citation_entry),            );        }        if let Some(ids_block) = extract_ids_block(&citation) {            for id in ids_block                .lines()                .map(str::trim)                .filter(|line| !line.is_empty())            {                if seen_rollout_ids.insert(id.to_string()) {                    rollout_ids.push(id.to_string());                }            }        }    }    if entries.is_empty() && rollout_ids.is_empty() {        None    } else {        Some(MemoryCitation {            entries,            rollout_ids,        })    }}pub fn thread_ids_from_memory_citation(memory_citation: &MemoryCitation) -> Vec<ThreadId> {    memory_citation        .rollout_ids        .iter()        .filter_map(|id| ThreadId::try_from(id.as_str()).ok())        .collect()}fn parse_memory_citation_entry(line: &str) -> Option<MemoryCitationEntry> {    let line = line.trim();    if line.is_empty() {        return None;    }    let (location, note) = line.rsplit_once("|note=[")?;    let note = note.strip_suffix(']')?.trim().to_string();    let (path, line_range) = location.rsplit_once(':')?;    let (line_start, line_end) = line_range.split_once('-')?;    Some(MemoryCitationEntry {        path: path.trim().to_string(),        line_start: line_start.trim().parse().ok()?,        line_end: line_end.trim().parse().ok()?,        note,    })}fn extract_block<'a>(text: &'a str, open: &str, close: &str) -> Option<&'a str> {    let (_, rest) = text.split_once(open)?;    let (body, _) = rest.split_once(close)?;    Some(body)}fn extract_ids_block(text: &str) -> Option<&str> {    extract_block(text, "<rollout_ids>", "</rollout_ids>")        .or_else(|| extract_block(text, "<thread_ids>", "</thread_ids>"))}#[cfg(test)]#[path = "citations_tests.rs"]mod tests;