Skip to main content

rskit_util/strings/
truncate.rs

1/// Safely truncates a string to a UTF-8-safe prefix sized for [`truncate_owned`].
2///
3/// The returned prefix is at most `max_bytes - 3` bytes when truncation is needed,
4/// reserving space for the ellipsis that [`truncate_owned`] appends.
5///
6/// Use [`truncate_owned`] if you need the truncated string with an appended ellipsis (`...`).
7/// # Examples
8///
9/// ```
10/// use rskit_util::strings::truncate;
11/// assert_eq!(truncate("hello world", 8), "hello");
12/// assert_eq!(truncate("hello", 10), "hello");
13/// assert_eq!(truncate("🦀🦀🦀🦀", 8), "🦀");
14/// ```
15pub fn truncate(s: &str, max_bytes: usize) -> &str {
16    if s.len() <= max_bytes {
17        return s;
18    }
19
20    // `truncate_owned` appends "..." (3 bytes), so `truncate` returns at most max_bytes - 3 bytes.
21    if max_bytes <= 3 {
22        return &s[..0]; // too short to include any prefix before the ellipsis
23    }
24
25    let limit = max_bytes - 3;
26    let mut index = limit;
27    while index > 0 && !s.is_char_boundary(index) {
28        index -= 1;
29    }
30
31    &s[..index]
32}
33
34/// Safely truncates an owned string to a max byte-length.
35///
36/// Truncated values include an ellipsis (`...`) when `max_bytes > 3`. For smaller limits,
37/// the result is `max_bytes` dots because there is not enough space for the full ellipsis.
38pub fn truncate_owned(s: &str, max_bytes: usize) -> String {
39    if s.len() <= max_bytes {
40        return s.to_string();
41    }
42    let truncated = truncate(s, max_bytes);
43    if truncated.is_empty() {
44        ".".repeat(max_bytes)
45    } else {
46        format!("{truncated}...")
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_truncate() {
56        assert_eq!(truncate("hello world", 8), "hello");
57        assert_eq!(truncate_owned("hello world", 8), "hello...");
58        assert_eq!(truncate_owned("hello", 10), "hello");
59        assert_eq!(truncate_owned("🦀🦀🦀🦀", 8), "🦀...");
60    }
61}