pub(crate) fn truncate_path(path: &str, max_len: usize) -> String {
if path.len() <= max_len {
path.to_string()
} else {
if let Some(last_slash) = path.rfind('/') {
let filename = &path[last_slash..];
if filename.len() >= max_len {
let available_chars = max_len.saturating_sub(3);
if available_chars == 0 {
"...".to_string()
} else {
let chars: Vec<char> = filename.chars().collect();
let end_idx = (chars.len()).min(available_chars + 3); let truncated: String = chars[3..end_idx].iter().collect(); format!("...{}", truncated)
}
} else {
let remaining_space = max_len.saturating_sub(filename.len()).saturating_sub(3);
if remaining_space == 0 {
format!("...{}", filename)
} else {
let path_chars: Vec<char> = path.chars().collect();
let path_start: String = path_chars[..remaining_space].iter().collect();
format!("{}...{}", path_start, filename)
}
}
} else {
let available_chars = max_len.saturating_sub(3);
if available_chars == 0 {
"...".to_string()
} else {
let chars: Vec<char> = path.chars().collect();
let truncated: String = chars[..available_chars].iter().collect();
format!("{}...", truncated)
}
}
}
}
pub(crate) fn truncate_string(s: &str, max_len: usize) -> String {
let chars: Vec<char> = s.chars().collect();
if chars.len() <= max_len {
s.to_string()
} else {
let available_chars = max_len.saturating_sub(3);
if available_chars == 0 {
"...".to_string()
} else {
let truncated: String = chars[..available_chars].iter().collect();
format!("{}...", truncated)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_path_no_truncation_needed() {
assert_eq!(truncate_path("short.txt", 20), "short.txt");
assert_eq!(truncate_path("./file.rs", 10), "./file.rs");
assert_eq!(truncate_path("a/b", 3), "a/b");
assert_eq!(truncate_path("", 10), "");
assert_eq!(truncate_path("a", 5), "a");
}
#[test]
fn test_truncate_path_basic_truncation() {
assert_eq!(
truncate_path("./path/very-long-path/file_name.pt", 25),
"./path/ve.../file_name.pt"
);
assert_eq!(
truncate_path("/usr/local/bin/program", 15),
"/usr.../program"
);
assert_eq!(
truncate_path("src/components/header.tsx", 20),
"src/co.../header.tsx"
);
}
#[test]
fn test_truncate_path_filename_takes_most_space() {
assert_eq!(
truncate_path("./long-path/long-filename.txt", 25),
"./lo.../long-filename.txt"
);
assert_eq!(
truncate_path("/a/b/c/d/important.config", 20),
".../important.config"
);
}
#[test]
fn test_truncate_path_filename_barely_fits() {
assert_eq!(
truncate_path("./very-long-path/file.txt", 12),
".../file.txt"
);
assert_eq!(truncate_path("/extremely/long/path/f.x", 10), "/ex.../f.x");
}
#[test]
fn test_truncate_path_no_slash() {
assert_eq!(
truncate_path("very-long-filename-without-slash", 15),
"very-long-fi..."
);
assert_eq!(truncate_path("filename.txt", 8), "filen...");
assert_eq!(truncate_path("toolong", 5), "to...");
}
#[test]
fn test_truncate_path_edge_cases_with_slashes() {
assert_eq!(truncate_path("../../../../file.txt", 15), "../.../file.txt");
assert_eq!(truncate_path("/", 5), "/");
assert_eq!(truncate_path("./", 5), "./");
assert_eq!(truncate_path("a/", 5), "a/");
assert_eq!(truncate_path("/a", 5), "/a");
}
#[test]
fn test_truncate_string_no_truncation_needed() {
assert_eq!(truncate_string("short", 10), "short");
assert_eq!(truncate_string("exact", 5), "exact");
assert_eq!(truncate_string("", 10), "");
assert_eq!(truncate_string("a", 5), "a");
assert_eq!(truncate_string("ab", 2), "ab");
}
#[test]
fn test_truncate_string_basic_truncation() {
assert_eq!(truncate_string("this is a long string", 10), "this is...");
assert_eq!(truncate_string("hello world", 8), "hello...");
assert_eq!(truncate_string("testing", 6), "tes...");
}
#[test]
fn test_truncate_string_minimal_length() {
assert_eq!(truncate_string("hello", 3), "...");
assert_eq!(truncate_string("toolong", 4), "t...");
assert_eq!(truncate_string("ab", 3), "ab");
}
#[test]
fn test_truncate_string_unicode() {
assert_eq!(truncate_string("café", 4), "café");
assert_eq!(truncate_string("hello 世界", 8), "hello 世界");
}
}