use super::funejson::Value; /// The Unicode White_Space property, spelled out so all three languages agree. /// It happens to match `char::is_whitespace`, but JavaScript's \s and Python's /// str.isspace draw the line differently, so the list is explicit everywhere. fn is_whitespace(ch: char) -> bool { let cp = ch as u32; (0x09..=0x0d).contains(&cp) || cp == 0x20 || cp == 0x85 || cp == 0xa0 || cp == 0x1680 || (0x2000..=0x200a).contains(&cp) || cp == 0x2028 || cp == 0x2029 || cp == 0x202f || cp == 0x205f || cp == 0x3000 } /// Punctuation that reads badly directly before an ellipsis. fn is_trailing_punctuation(ch: char) -> bool { matches!(ch, ',' | ';' | ':' | '-' | '.') } /// Shorten `value` to at most `max_length` code points, ellipsis included, /// cutting at a word boundary where there is one. /// /// # Panics /// Panics if `max_length` is negative or shorter than the ellipsis. pub fn truncate(value: &str, max_length: i64, ellipsis: &str) -> String { if max_length < 0 { panic!("maxLength must be 0 or greater, received {}", max_length); } let chars: Vec = value.chars().collect(); let mark_len = ellipsis.chars().count() as i64; if mark_len > max_length { panic!( "maxLength must be at least the length of the ellipsis, received {}", max_length ); } if chars.len() as i64 <= max_length { return value.to_string(); } let budget = (max_length - mark_len) as usize; let mut end = budget; // A cut just before whitespace is already a clean boundary. if !is_whitespace(chars[budget]) { // No boundary to move back to (none, or only at position 0): one long // word is cut hard rather than reduced to a bare ellipsis. if let Some(last_space) = (0..budget).rev().find(|&i| is_whitespace(chars[i])) { if last_space > 0 { end = last_space; } } } while end > 0 && (is_whitespace(chars[end - 1]) || is_trailing_punctuation(chars[end - 1])) { end -= 1; } let mut out: String = chars[..end].iter().collect(); out.push_str(ellipsis); out } pub fn fune_vector(args: &[Value]) -> Value { Value::Str(truncate(args[0].as_str(), args[1].as_i64(), args[2].as_str())) }