pub const MAX_CONTENT_LENGTH: usize = 10_000;
pub const NOISY_TOOL_MAX_LENGTH: usize = 2_000;
pub fn truncate(content: &str, max_length: usize) -> String {
let (prefix, total_units) = utf16_prefix(content, max_length);
if total_units <= max_length {
return content.to_string();
}
format!("{prefix}\n[Truncated {} chars]", total_units - max_length)
}
fn utf16_prefix(content: &str, max_units: usize) -> (&str, usize) {
let mut units = 0;
for (byte_idx, ch) in content.char_indices() {
let ch_units = ch.len_utf16();
if units + ch_units > max_units {
let total = units
+ content[byte_idx..]
.chars()
.map(char::len_utf16)
.sum::<usize>();
return (&content[..byte_idx], total);
}
units += ch_units;
}
(content, units)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_content_is_unchanged() {
assert_eq!(truncate("hello", 10), "hello");
assert_eq!(truncate("", 10), "");
}
#[test]
fn content_at_exactly_the_limit_is_unchanged() {
let content = "x".repeat(10);
assert_eq!(truncate(&content, 10), content);
}
#[test]
fn long_content_gets_the_node_notice() {
let content = "x".repeat(15);
assert_eq!(
truncate(&content, 10),
format!("{}\n[Truncated 5 chars]", "x".repeat(10))
);
}
#[test]
fn length_counts_utf16_units_like_javascript() {
let content = "🦀🦀🦀";
assert_eq!(truncate(content, 6), content);
assert_eq!(truncate(content, 4), "🦀🦀\n[Truncated 2 chars]");
assert_eq!(truncate(content, 5), "🦀🦀\n[Truncated 1 chars]");
}
}