use crate::session;
use crate::store;
use crate::util::format_age;
pub const SNIPPET_CAP: usize = 512;
pub const ELLIPSIS_MARKER: &str = " [oo: truncated]";
pub fn display_hit(content: &str, snippet: &Option<String>, cap: usize) -> String {
match snippet {
Some(snip) => bounded_display(snip, cap),
None => bounded_display(content, cap),
}
}
pub fn bounded_display(content: &str, cap: usize) -> String {
let char_count = content.chars().count();
if char_count <= cap {
return content.to_string();
}
let truncated: String = content.chars().take(cap).collect();
format!("{truncated}{ELLIPSIS_MARKER}")
}
pub fn cmd_recall(query: &str, full: bool) -> i32 {
if query.is_empty() {
eprintln!("oo: recall requires a query");
return 1;
}
let mut store = match store::open() {
Ok(s) => s,
Err(e) => {
eprintln!("oo: {e}");
return 1;
}
};
let project_id = session::project_id();
match store.search(&project_id, query, 5) {
Ok(results) if results.is_empty() => {
println!("No results found.");
0
}
Ok(results) => {
for r in &results {
if let Some(meta) = &r.meta {
let age = format_age(meta.timestamp);
println!("[session] {} ({age}):", meta.command);
} else {
println!("[memory] project memory:");
}
if full {
for line in r.content.lines() {
println!(" {line}");
}
} else {
let display = display_hit(&r.content, &r.snippet, SNIPPET_CAP);
for line in display.lines() {
println!(" {line}");
}
}
println!();
}
0
}
Err(e) => {
eprintln!("oo: {e}");
1
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_content_unchanged() {
let input = "short";
assert_eq!(bounded_display(input, 512), input);
}
#[test]
fn content_exactly_at_cap_unchanged() {
let input: String = "x".repeat(512);
assert_eq!(bounded_display(&input, 512), input);
}
#[test]
fn content_over_cap_truncated_with_marker() {
let input: String = "x".repeat(600);
let result = bounded_display(&input, 512);
assert_eq!(result.chars().count(), 512 + 16);
assert!(result.ends_with(ELLIPSIS_MARKER));
assert!(!result.ends_with(&"x".repeat(100)));
}
#[test]
fn truncation_marker_is_unambiguous_sentinel() {
assert_eq!(ELLIPSIS_MARKER, " [oo: truncated]");
assert!(ELLIPSIS_MARKER.contains('['));
}
#[test]
fn display_hit_prefers_snippet_when_present() {
let content: String = (0..200)
.map(|i| format!("line{i:04}_padding_{} ", "x".repeat(40)))
.collect();
let snippet = "…line0100_padding_".to_string() + &"y".repeat(50) + "…";
let display = display_hit(&content, &Some(snippet.clone()), SNIPPET_CAP);
assert_eq!(
display, snippet,
"display must be the store-provided snippet when one is present"
);
assert!(
!content.starts_with(&snippet) && !content.starts_with(&display),
"display must not be a prefix of the full content — that would prove \nthe FTS5 snippet was thrown away in favour of content"
);
}
#[test]
fn display_hit_snippet_oversized_still_bounded_and_marked() {
let content: String = std::iter::repeat('€').take(10_000).collect();
let snippet: String = std::iter::repeat('€').take(10_000).collect();
let display = display_hit(&content, &Some(snippet), SNIPPET_CAP);
assert_eq!(
display.chars().count(),
SNIPPET_CAP + ELLIPSIS_MARKER.chars().count()
);
assert!(display.ends_with(ELLIPSIS_MARKER));
}
#[test]
fn display_hit_falls_back_to_content_when_snippet_none() {
let content: String = (0..200).map(|i| format!("word{i} ")).collect();
let display = display_hit(&content, &None, SNIPPET_CAP);
assert_eq!(display, bounded_display(&content, SNIPPET_CAP));
assert!(
display.starts_with("word0"),
"fallback must be a prefix of content"
);
}
#[test]
fn multi_byte_chars_respect_char_boundary() {
let input: String = std::iter::repeat('€').take(100).collect();
assert_eq!(bounded_display(&input, 512), input);
let long: String = std::iter::repeat('€').take(600).collect();
let result = bounded_display(&long, 512);
assert_eq!(
result.chars().count(),
512 + ELLIPSIS_MARKER.chars().count()
);
assert!(result.ends_with(ELLIPSIS_MARKER));
}
}