use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
pub fn truncate_str(s: &str, max_width: usize) -> String {
let width = s.width();
if width <= max_width {
return s.to_string();
}
let target_width = max_width.saturating_sub(3); let mut current_width = 0;
let mut end_idx = 0;
for (idx, ch) in s.char_indices() {
let ch_width = ch.width().unwrap_or(0);
if current_width + ch_width > target_width {
break;
}
current_width += ch_width;
end_idx = idx + ch.len_utf8();
}
format!("{}...", &s[..end_idx])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_str_ascii() {
assert_eq!(truncate_str("hello world", 8), "hello...");
assert_eq!(truncate_str("short", 10), "short");
assert_eq!(truncate_str("exact len", 9), "exact len");
}
#[test]
fn test_truncate_str_japanese() {
assert_eq!(truncate_str("日本語", 10), "日本語"); assert_eq!(truncate_str("日本語テスト", 10), "日本語..."); }
#[test]
fn test_truncate_str_mixed() {
assert_eq!(truncate_str("Hello世界", 10), "Hello世界"); assert_eq!(truncate_str("Hello世界!", 10), "Hello世界!"); assert_eq!(truncate_str("Hello世界!!", 10), "Hello世..."); }
#[test]
fn test_truncate_str_empty() {
assert_eq!(truncate_str("", 10), "");
assert_eq!(truncate_str("", 0), "");
}
#[test]
fn test_truncate_str_small_max() {
assert_eq!(truncate_str("hello", 3), "...");
assert_eq!(truncate_str("hello", 4), "h...");
}
}