pub fn cell_len(text: &str) -> usize {
unicode_width::UnicodeWidthStr::width(text)
}
pub fn get_character_cell_size(ch: char) -> usize {
unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0)
}
pub fn set_cell_size(text: &str, target_width: usize) -> String {
let current = cell_len(text);
if current == target_width {
return text.to_string();
}
if current > target_width {
let mut out = String::new();
let mut w = 0usize;
for ch in text.chars() {
let cw = get_character_cell_size(ch);
if w + cw > target_width {
out.push_str(&" ".repeat(target_width - w));
return out;
}
out.push(ch);
w += cw;
}
out
} else {
format!("{}{}", text, " ".repeat(target_width - current))
}
}
pub fn chop_cells(text: &str, width: usize) -> Vec<String> {
if width == 0 {
return vec![text.to_string()];
}
let mut lines: Vec<String> = Vec::new();
let mut current = String::new();
let mut current_w = 0usize;
for ch in text.chars() {
let cw = get_character_cell_size(ch);
if ch == '\n' {
lines.push(current);
current = String::new();
current_w = 0;
continue;
}
if current_w + cw > width {
lines.push(current);
current = String::new();
current_w = 0;
}
current.push(ch);
current_w += cw;
}
if !current.is_empty() {
lines.push(current);
}
if lines.is_empty() {
lines.push(String::new());
}
lines
}
pub fn split_text(text: &str, cell_offset: usize) -> (String, String) {
let mut left = String::new();
let mut right = String::new();
let mut w = 0usize;
let mut passed = false;
for ch in text.chars() {
let cw = get_character_cell_size(ch);
if passed {
right.push(ch);
} else if w + cw > cell_offset {
left.push_str(&" ".repeat(cell_offset - w));
right.push(ch);
passed = true;
} else {
left.push(ch);
w += cw;
if w == cell_offset {
passed = true;
}
}
}
(left, right)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cell_len_ascii() {
assert_eq!(cell_len("hello"), 5);
}
#[test]
fn test_cell_len_cjk() {
assert_eq!(cell_len("你好"), 4); }
#[test]
fn test_cell_len_emoji() {
assert_eq!(cell_len("🎉"), 2);
}
#[test]
fn test_set_cell_size_pad() {
assert_eq!(set_cell_size("hi", 10), "hi ");
}
#[test]
fn test_set_cell_size_crop() {
assert_eq!(set_cell_size("hello world", 5), "hello");
}
#[test]
fn test_set_cell_size_exact() {
assert_eq!(set_cell_size("hello", 5), "hello");
}
#[test]
fn test_chop_cells_basic() {
let lines = chop_cells("hello world", 5);
assert_eq!(lines, vec!["hello", " worl", "d"]);
}
#[test]
fn test_chop_cells_newline() {
let lines = chop_cells("a\nb\nc", 10);
assert_eq!(lines, vec!["a", "b", "c"]);
}
#[test]
fn test_split_text_middle() {
let (left, right) = split_text("hello world", 5);
assert_eq!(left, "hello");
assert_eq!(right, " world");
}
#[test]
fn test_split_text_past_end() {
let (left, right) = split_text("hi", 10);
assert_eq!(left, "hi");
assert_eq!(right, "");
}
}