const TRUNCATE_LIMIT: usize = 256;
fn floor_char_boundary(s: &str, index: usize) -> usize {
if index >= s.len() {
s.len()
} else {
let mut i = index;
while i > 0 && !s.is_char_boundary(i) {
i -= 1;
}
i
}
}
pub fn truncate_for_log(s: &str) -> String {
if s.len() <= TRUNCATE_LIMIT {
s.to_string()
} else {
format!(
"{}... [truncated, total {} bytes]",
&s[..floor_char_boundary(s, TRUNCATE_LIMIT)],
s.len()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_string_unchanged() {
let s = "hello world";
assert_eq!(truncate_for_log(s), s);
}
#[test]
fn exactly_at_limit() {
let s = "a".repeat(TRUNCATE_LIMIT);
assert_eq!(truncate_for_log(&s), s);
}
#[test]
fn over_limit_truncated() {
let s = "a".repeat(TRUNCATE_LIMIT + 100);
let result = truncate_for_log(&s);
assert!(result.contains("... [truncated, total"));
assert!(result.contains(&format!("{} bytes]", TRUNCATE_LIMIT + 100)));
assert!(result.len() < s.len());
}
#[test]
fn multibyte_chars_safe() {
let s = "你".repeat(200); let result = truncate_for_log(&s);
assert!(result.contains("... [truncated, total"));
}
}