1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
fn find_char_bytes_len(ch: &char) -> i32 { let mut b = [0; 4]; ch.encode_utf8(&mut b); let mut clen = 0; for a in b.iter() { clen += match a { 0 => 0, _ => 1, } } clen } pub fn truncate_text(text: &str, tlen: usize) -> &str { if text.len() <= tlen { return text; } let c = text.chars().nth(tlen); match c { Some(s) => match char::is_whitespace(s) { true => text.split_at(tlen).0, false => { let chars: Vec<_> = text.chars().collect(); let truncated = chars.split_at(tlen); let mut first_len = 0; for ch in truncated.0.iter() { first_len += find_char_bytes_len(ch); } let mut prev_ws = first_len - 1; for ch in truncated.0.iter().rev() { if char::is_whitespace(*ch) { break; } prev_ws -= find_char_bytes_len(ch); } let mut next_ws = first_len + 1; for ch in truncated.1.iter() { let mut b = [0; 4]; ch.encode_utf8(&mut b); if char::is_whitespace(*ch) { break; } next_ws += find_char_bytes_len(ch); } match next_ws > prev_ws && prev_ws > 0 { true => text.split_at(prev_ws as usize).0, false => text.split_at(next_ws as usize).1, } } }, None => text, } } #[cfg(test)] mod tests { use super::*; #[test] fn returns_on_short_string() { assert_eq!(truncate_text("test", 7), "test"); } #[test] fn truncates_on_whitespace_exact() { assert_eq!(truncate_text("test test", 4), "test"); } #[test] fn truncates_on_whitespace_after() { assert_eq!(truncate_text("test test", 3), "test"); } #[test] fn truncates_on_whitespace_before() { assert_eq!(truncate_text("test test", 5), "test"); } #[test] fn truncates_on_whitespace_unicode() { assert_eq!(truncate_text("te😀t test", 5), "te😀t"); } #[test] fn truncates_on_whitespace_unicode2() { assert_eq!(truncate_text("te😀t test", 3), "test"); } }