1pub fn truncate_bytes_safe(s: &str, max_bytes: usize) -> &str {
12 if s.len() <= max_bytes {
13 return s;
14 }
15 let mut end = max_bytes;
16 while end > 0 && !s.is_char_boundary(end) {
17 end -= 1;
18 }
19 &s[..end]
20}
21
22#[cfg(test)]
23mod tests {
24 use super::*;
25
26 #[test]
27 fn ascii_shorter_than_limit() {
28 assert_eq!(truncate_bytes_safe("hello", 10), "hello");
29 }
30
31 #[test]
32 fn ascii_exactly_at_limit() {
33 assert_eq!(truncate_bytes_safe("hello", 5), "hello");
34 }
35
36 #[test]
37 fn ascii_truncated() {
38 assert_eq!(truncate_bytes_safe("hello world", 5), "hello");
39 }
40
41 #[test]
42 fn multibyte_on_boundary() {
43 let s = "abc\u{2014}def";
45 assert_eq!(truncate_bytes_safe(s, 6), "abc\u{2014}");
46 }
47
48 #[test]
49 fn multibyte_cut_inside_char_does_not_panic() {
50 let prefix = "a".repeat(299);
52 let s = format!("{prefix}\u{2014}trailing");
53 let result = truncate_bytes_safe(&s, 300);
55 assert_eq!(result, prefix.as_str());
56 }
57
58 #[test]
59 fn zero_limit_returns_empty() {
60 assert_eq!(truncate_bytes_safe("hello", 0), "");
61 }
62}