use anyhow::Result;
use crate::utils::tokens::{estimate_tokens, truncate_to_tokens};
pub mod execution;
pub mod file_ops;
pub mod search;
pub(super) fn truncate_line(line: &str, max_len: usize) -> String {
if line.len() <= max_len {
line.to_string()
} else {
let target = max_len.saturating_sub(3);
let end = line
.char_indices()
.map(|(i, _)| i)
.rfind(|&i| i <= target)
.unwrap_or(0);
format!("{}...", &line[..end])
}
}
pub trait Summarizer {
fn summarize(&self, full_output: &str, metadata: Option<&serde_json::Value>) -> Result<String>;
fn estimate_savings(&self, full_output: &str, summary: &str) -> (usize, usize, f32) {
let ui_tokens = estimate_tokens(full_output);
let llm_tokens = estimate_tokens(summary);
let savings = ui_tokens.saturating_sub(llm_tokens);
let savings_pct = if ui_tokens > 0 {
(savings as f32 / ui_tokens as f32) * 100.0
} else {
0.0
};
(llm_tokens, ui_tokens, savings_pct)
}
}
pub fn extract_key_info(text: &str, max_lines: usize) -> String {
let mut lines: Vec<&str> = Vec::with_capacity(max_lines.min(32));
let mut total_lines = 0usize;
for line in text.lines() {
total_lines += 1;
if lines.len() < max_lines {
lines.push(line);
}
}
if total_lines > max_lines {
format!(
"{}\n[...{} more lines]",
lines.join("\n"),
total_lines - max_lines
)
} else {
lines.join("\n")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_estimate_tokens() {
assert_eq!(estimate_tokens("Hello world"), 3); assert_eq!(estimate_tokens(""), 0);
assert_eq!(estimate_tokens("a".repeat(1000).as_str()), 250); }
#[test]
fn test_truncate_to_tokens() {
let text = "a".repeat(1000);
let truncated = truncate_to_tokens(&text, 50); assert!(truncated.len() <= 203); assert!(truncated.ends_with("..."));
}
#[test]
fn test_truncate_short_text() {
let text = "Short text";
let truncated = truncate_to_tokens(text, 100);
assert_eq!(truncated, text);
}
#[test]
fn test_extract_key_info() {
let text = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5";
let extracted = extract_key_info(text, 3);
assert!(extracted.contains("Line 1"));
assert!(extracted.contains("Line 3"));
assert!(extracted.contains("[...2 more lines]"));
}
#[test]
fn test_extract_key_info_exact() {
let text = "Line 1\nLine 2\nLine 3";
let extracted = extract_key_info(text, 3);
assert_eq!(extracted, text);
assert!(!extracted.contains("more lines"));
}
}