#![forbid(unsafe_code)]
#[must_use]
pub fn truncate_utf8(content: &str, max_chars: usize) -> (String, bool) {
if max_chars == 0 {
return (String::new(), !content.is_empty());
}
if content.len() <= max_chars {
return (content.to_string(), false);
}
match content.char_indices().nth(max_chars) {
None => (content.to_string(), false),
Some((idx, _)) => (content[..idx].to_string(), true),
}
}
#[must_use]
pub(crate) fn take_utf8_capped(bytes: Vec<u8>, max_chars: usize) -> (String, bool) {
let s = match String::from_utf8(bytes) {
Ok(s) => s,
Err(e) => String::from_utf8_lossy(e.as_bytes()).into_owned(),
};
if max_chars == 0 {
return (String::new(), !s.is_empty());
}
if s.len() <= max_chars {
return (s, false);
}
match s.char_indices().nth(max_chars) {
None => (s, false),
Some((idx, _)) => {
let mut owned = s;
owned.truncate(idx);
(owned, true)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn truncate_utf8_no_truncate_if_fits() {
let (s, t) = truncate_utf8("ola mundo", 100);
assert_eq!(s, "ola mundo");
assert!(!t);
}
#[test]
fn truncate_utf8_truncates_large_ascii() {
let entrada: String = "a".repeat(200);
let (s, t) = truncate_utf8(&entrada, 50);
assert_eq!(s.chars().count(), 50);
assert!(t);
}
#[test]
fn truncate_utf8_preserves_accented_graphemes() {
let entrada: String = "á".repeat(30);
let (s, t) = truncate_utf8(&entrada, 10);
assert_eq!(s.chars().count(), 10);
assert_eq!(s.len(), 20);
assert!(t);
assert!(s.chars().all(|c| c == 'á'));
}
#[test]
fn truncate_utf8_emojis_does_not_break() {
let entrada = "🚀🔒🛡🔑✨🎉💎⚡🌟🔥🎨";
let (s, t) = truncate_utf8(entrada, 5);
assert_eq!(s.chars().count(), 5);
assert!(t);
}
#[test]
fn truncate_utf8_zero_returns_empty() {
let (s, t) = truncate_utf8("abc", 0);
assert_eq!(s, "");
assert!(t);
}
#[test]
fn take_utf8_capped_reuses_valid_utf8_without_truncate() {
let bytes = b"hello agent".to_vec();
let (s, t) = take_utf8_capped(bytes, 100);
assert_eq!(s, "hello agent");
assert!(!t);
}
#[test]
fn take_utf8_capped_truncates_and_replaces_invalid() {
let mut bytes = b"abcdef".to_vec();
let (s, t) = take_utf8_capped(bytes.clone(), 3);
assert_eq!(s, "abc");
assert!(t);
bytes = vec![0xFF, b'a', b'b'];
let (s, t) = take_utf8_capped(bytes, 10);
assert!(!s.is_empty());
assert!(!t);
assert!(s.contains('a'));
}
}