pub fn truncate_str(s: &str, max: usize) -> String {
if s.len() <= max {
s.to_string()
} else {
let target = max.saturating_sub(3);
let mut end = target;
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
format!("{}...", &s[..end])
}
}
pub fn truncate_path(s: &str, max: usize) -> String {
if s.len() <= max {
s.to_string()
} else {
let target = s.len() - max + 3;
let mut start = target;
while start < s.len() && !s.is_char_boundary(start) {
start += 1;
}
format!("...{}", &s[start..])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_str_ascii() {
assert_eq!(truncate_str("hello", 10), "hello");
assert_eq!(truncate_str("hello", 5), "hello");
assert_eq!(truncate_str("hello world", 8), "hello...");
assert_eq!(truncate_str("abcdefghij", 7), "abcd...");
}
#[test]
fn test_truncate_str_unicode() {
let box_line = "┌────────────────────┐";
let result = truncate_str(box_line, 10);
assert!(result.ends_with("..."));
let emoji = "Hello 🎉🎊🎁 World";
let result = truncate_str(emoji, 10);
assert!(result.ends_with("..."));
let chinese = "你好世界测试";
let result = truncate_str(chinese, 8);
assert!(result.ends_with("..."));
}
#[test]
fn test_truncate_path_ascii() {
assert_eq!(truncate_path("src/main.rs", 20), "src/main.rs");
let result = truncate_path("/very/long/path/to/file.rs", 15);
assert!(result.starts_with("..."));
assert!(result.contains("file.rs"));
}
#[test]
fn test_truncate_path_unicode() {
let path = "/home/用户/项目/文件.rs";
let result = truncate_path(path, 15);
assert!(result.starts_with("..."));
let path = "/home/📁/🎉/file.rs";
let result = truncate_path(path, 12);
assert!(result.starts_with("..."));
}
#[test]
fn test_truncate_edge_cases() {
assert_eq!(truncate_str("hello", 3), "...");
assert_eq!(truncate_str("hi", 3), "hi");
assert_eq!(truncate_str("", 10), "");
assert_eq!(truncate_path("", 10), "");
}
}