use anyhow::Result;
pub fn calculate_text_lines(text: &str) -> Result<usize> {
let terminal_width = crossterm::terminal::size()
.map_err(|e| anyhow::anyhow!("Failed to get terminal size: {}", e))?
.0 as usize;
Ok(calculate_text_lines_with_width(text, terminal_width))
}
pub fn calculate_text_lines_with_width(text: &str, terminal_width: usize) -> usize {
if text.is_empty() || terminal_width == 0 {
return 1;
}
let mut total_lines = 0;
for line in text.split('\n') {
if line.is_empty() {
total_lines += 1;
} else {
let line_len = line.len();
let lines_for_this_line = (line_len + terminal_width - 1) / terminal_width; total_lines += lines_for_this_line;
}
}
if total_lines == 0 { 1 } else { total_lines }
}
pub fn split_text_into_lines(text: &str) -> Result<Vec<String>> {
let terminal_width = crossterm::terminal::size()
.map_err(|e| anyhow::anyhow!("Failed to get terminal size: {}", e))?
.0 as usize;
Ok(split_text_into_lines_with_width(text, terminal_width))
}
pub fn split_text_into_lines_with_width(text: &str, terminal_width: usize) -> Vec<String> {
if text.is_empty() || terminal_width == 0 {
return vec![String::new()];
}
let mut result = Vec::new();
for line in text.split('\n') {
if line.is_empty() {
result.push(String::new());
} else {
let chars: Vec<char> = line.chars().collect();
let mut start = 0;
while start < chars.len() {
let end = std::cmp::min(start + terminal_width, chars.len());
let chunk: String = chars[start..end].iter().collect();
result.push(chunk);
start = end;
}
}
}
if result.is_empty() {
vec![String::new()]
} else {
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_cases() {
assert_eq!(calculate_text_lines_with_width("", 80), 1);
assert_eq!(calculate_text_lines_with_width("hello", 80), 1);
assert_eq!(calculate_text_lines_with_width("hello world", 80), 1);
}
#[test]
fn test_exact_width_boundaries() {
assert_eq!(calculate_text_lines_with_width("1234567890", 10), 1);
assert_eq!(calculate_text_lines_with_width("12345678901", 10), 2);
assert_eq!(
calculate_text_lines_with_width("12345678901234567890", 10),
2
);
assert_eq!(
calculate_text_lines_with_width("123456789012345678901", 10),
3
);
}
#[test]
fn test_newlines() {
assert_eq!(calculate_text_lines_with_width("line1\nline2", 80), 2);
assert_eq!(
calculate_text_lines_with_width("line1\nline2\nline3", 80),
3
);
assert_eq!(calculate_text_lines_with_width("line1\n\nline3", 80), 3);
assert_eq!(
calculate_text_lines_with_width("12345678901\n12345678901", 10),
4
); }
#[test]
fn test_description_case() {
let desc = "Description: sad;lkfjsad;ljfsal;djkf;laskdjf;lkasdj;flkjasdl;kfjasd;lkjfas;lkdjf;lksajdfl;jkasd;lkf";
let width = 80;
let result = calculate_text_lines_with_width(desc, width);
assert_eq!(result, 2);
assert_eq!(calculate_text_lines_with_width(desc, 50), 2); assert_eq!(calculate_text_lines_with_width(desc, 40), 3); assert_eq!(calculate_text_lines_with_width(desc, 100), 1); }
#[test]
fn test_zero_width() {
assert_eq!(calculate_text_lines_with_width("hello", 0), 1);
}
#[test]
fn test_realistic_scenarios() {
assert_eq!(
calculate_text_lines_with_width("Description: User clicks button", 80),
1
);
let long_desc = format!("Description: {}", "a".repeat(70));
assert_eq!(calculate_text_lines_with_width(&long_desc, 80), 2);
let very_long = format!("Description: {}", "a".repeat(200));
assert_eq!(calculate_text_lines_with_width(&very_long, 80), 3); }
#[test]
fn test_split_text_simple() {
assert_eq!(split_text_into_lines_with_width("", 10), vec![""]);
assert_eq!(split_text_into_lines_with_width("hello", 10), vec!["hello"]);
assert_eq!(
split_text_into_lines_with_width("1234567890", 10),
vec!["1234567890"]
);
assert_eq!(
split_text_into_lines_with_width("12345678901", 10),
vec!["1234567890", "1"]
);
}
#[test]
fn test_split_text_with_newlines() {
assert_eq!(
split_text_into_lines_with_width("line1\nline2", 10),
vec!["line1", "line2"]
);
assert_eq!(
split_text_into_lines_with_width("a\nb\nc", 10),
vec!["a", "b", "c"]
);
assert_eq!(
split_text_into_lines_with_width("a\n\nb", 10),
vec!["a", "", "b"]
);
assert_eq!(
split_text_into_lines_with_width("12345678901\n12345678901", 10),
vec!["1234567890", "1", "1234567890", "1"]
);
}
#[test]
fn test_split_text_description_case() {
let desc = "Description: hello world test";
let result = split_text_into_lines_with_width(desc, 20);
assert_eq!(result, vec!["Description: hello w", "orld test"]);
let exact = "12345678901234567890"; assert_eq!(
split_text_into_lines_with_width(exact, 20),
vec!["12345678901234567890"]
);
let over = "123456789012345678901"; assert_eq!(
split_text_into_lines_with_width(over, 20),
vec!["12345678901234567890", "1"]
);
}
#[test]
fn test_split_text_zero_width() {
assert_eq!(split_text_into_lines_with_width("hello", 0), vec![""]);
}
#[test]
fn test_split_text_unicode() {
let box_line = "┌────────────┐";
let result = split_text_into_lines_with_width(box_line, 10);
assert_eq!(result, vec!["┌─────────", "───┐"]);
let mixed = "Hello ─── World"; let result = split_text_into_lines_with_width(mixed, 8);
assert_eq!(result, vec!["Hello ──", "─ World"]);
let emoji = "Hello 🌟 World 🚀"; let result = split_text_into_lines_with_width(emoji, 10);
assert_eq!(result, vec!["Hello 🌟 Wo", "rld 🚀"]);
let failing_line = "19│└────────────────────────────┘";
let result = split_text_into_lines_with_width(failing_line, 84);
assert!(result.len() >= 1);
let unicode_heavy = "🌟🚀🎉🔥💯⭐🌈🎯";
let result = split_text_into_lines_with_width(unicode_heavy, 3);
assert_eq!(result, vec!["🌟🚀🎉", "🔥💯⭐", "🌈🎯"]);
}
}