use unicode_width::UnicodeWidthStr;
use unicode_width::UnicodeWidthChar;
pub fn wrap_text(text: &str, max_width: u16) -> Vec<String> {
if max_width == 0 {
return vec![];
}
let max_w = max_width as usize;
let mut lines = Vec::new();
for paragraph in text.split('\n') {
if paragraph.is_empty() {
lines.push(String::new());
continue;
}
let mut current_line = String::new();
let mut current_width: usize = 0;
for word in paragraph.split_whitespace() {
let word_width = word.width();
if current_line.is_empty() {
if word_width > max_w {
let mut w = 0;
let mut part = String::new();
for ch in word.chars() {
let cw = ch.width().unwrap_or(0);
if w + cw > max_w && !part.is_empty() {
lines.push(part);
part = String::new();
w = 0;
}
part.push(ch);
w += cw;
}
current_line = part;
current_width = w;
} else {
current_line = word.to_string();
current_width = word_width;
}
} else if current_width + 1 + word_width > max_w {
lines.push(current_line);
current_line = word.to_string();
current_width = word_width;
} else {
current_line.push(' ');
current_line.push_str(word);
current_width += 1 + word_width;
}
}
if !current_line.is_empty() {
lines.push(current_line);
}
}
if lines.is_empty() {
lines.push(String::new());
}
lines
}
pub fn display_width(text: &str) -> usize {
text.width()
}
pub fn truncate_to_width(text: &str, max_width: usize) -> String {
let text_w = text.width();
if text_w <= max_width {
return text.to_string();
}
let mut s = String::new();
let mut w = 0;
for ch in text.chars() {
let cw = UnicodeWidthChar::width(ch).unwrap_or(0);
if w + cw > max_width {
break;
}
s.push(ch);
w += cw;
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_wrap() {
let lines = wrap_text("hello world", 5);
assert_eq!(lines, vec!["hello", "world"]);
}
#[test]
fn test_no_wrap_needed() {
let lines = wrap_text("short", 20);
assert_eq!(lines, vec!["short"]);
}
#[test]
fn test_paragraph_breaks() {
let lines = wrap_text("line1\nline2\nline3", 20);
assert_eq!(lines, vec!["line1", "line2", "line3"]);
}
#[test]
fn test_empty_paragraph() {
let lines = wrap_text("before\n\nafter", 20);
assert_eq!(lines, vec!["before", "", "after"]);
}
#[test]
fn test_long_word_break() {
let lines = wrap_text("abcdefghij", 4);
assert_eq!(lines, vec!["abcd", "efgh", "ij"]);
}
#[test]
fn test_zero_width() {
let lines = wrap_text("hello", 0);
assert!(lines.is_empty());
}
#[test]
fn test_empty_text() {
let lines = wrap_text("", 10);
assert_eq!(lines, vec![""]);
}
#[test]
fn test_cjk_width() {
let lines = wrap_text("你好世界", 5);
assert_eq!(lines, vec!["你好", "世界"]);
}
#[test]
fn test_mixed_content() {
let lines = wrap_text("hello world\n\nrust is great", 12);
assert_eq!(lines, vec!["hello world", "", "rust is", "great"]);
}
}