//! Thin wrapper around the `unicode-width` crate.
//!
//! We go through this indirection rather than calling the crate directly so
//! we can inject a test hook for the table renderer.
use unicode_width::UnicodeWidthStr;
/// Return the display width of `s` in terminal cells.
///
/// Unknown / zero-width characters count as zero. Control characters count as
/// zero as well (they'd mangle alignment anyway).
pub fn display_width(s: &str) -> usize {
UnicodeWidthStr::width(s)
}
/// Truncate `s` to `budget` cells, returning the truncated substring. When
/// the input already fits, the original slice is returned unchanged.
pub fn truncate_to(s: &str, budget: usize) -> &str {
if display_width(s) <= budget {
return s;
}
let mut end = 0;
let mut used = 0;
for (i, ch) in s.char_indices() {
let w = UnicodeWidthStr::width(ch.encode_utf8(&mut [0u8; 4]) as &str);
if used + w > budget {
return &s[..end];
}
used += w;
end = i + ch.len_utf8();
}
&s[..end]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ascii_width() {
assert_eq!(display_width("hello"), 5);
}
#[test]
fn cjk_doubles_width() {
assert_eq!(display_width("日本語"), 6);
}
#[test]
fn truncate_ascii() {
assert_eq!(truncate_to("hello world", 5), "hello");
}
#[test]
fn truncate_cjk_respects_cell_width() {
assert_eq!(truncate_to("日本語", 4), "日本");
}
}