use unicode_segmentation::UnicodeSegmentation;
use super::{cluster_width, is_control_cluster, width};
const ELLIPSIS: &str = "\u{2026}";
pub fn truncate_ellipsis(s: &str, max_width: i32) -> String {
if max_width <= 0 {
return String::new();
}
if width(s) <= max_width {
return s
.graphemes(true)
.filter(|c| !is_control_cluster(c))
.collect();
}
let budget = max_width - 1; let mut out = String::new();
let mut used = 0i32;
for cluster in s.graphemes(true) {
if is_control_cluster(cluster) {
continue;
}
let w = cluster_width(cluster);
if used + w > budget {
break;
}
out.push_str(cluster);
used += w;
}
out.push_str(ELLIPSIS);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fits_untouched() {
assert_eq!(truncate_ellipsis("hello", 5), "hello");
assert_eq!(truncate_ellipsis("hello", 10), "hello");
assert_eq!(truncate_ellipsis("", 3), "");
}
#[test]
fn truncates_with_ellipsis() {
assert_eq!(truncate_ellipsis("hello world", 8), "hello w…");
assert_eq!(super::super::width(&truncate_ellipsis("hello world", 8)), 8);
}
#[test]
fn wide_cluster_never_straddles_the_cut() {
assert_eq!(truncate_ellipsis("世界人", 3), "世…");
assert_eq!(truncate_ellipsis("世界人", 4), "世…");
assert!(super::super::width(&truncate_ellipsis("世界人", 4)) <= 4);
}
#[test]
fn degenerate_widths() {
assert_eq!(truncate_ellipsis("hello", 0), "");
assert_eq!(truncate_ellipsis("hello", 1), "…");
}
#[test]
fn strips_controls_even_when_fitting() {
assert_eq!(truncate_ellipsis("a\tb", 5), "ab");
}
}