Skip to main content

apollo/
text.rs

1//! Small text utilities shared across tools and providers.
2
3pub fn truncate_chars(text: &str, max_chars: usize) -> String {
4    text.chars().take(max_chars).collect()
5}
6
7/// Truncate to `max_chars` characters.
8///
9/// Returns `None` when the text already fits, otherwise the truncated text
10/// and the number of characters dropped. The gate and the truncation use the
11/// same unit so the reported count is always accurate.
12pub fn truncate_chars_counted(text: &str, max_chars: usize) -> Option<(String, usize)> {
13    let total = text.chars().count();
14    if total <= max_chars {
15        return None;
16    }
17    Some((truncate_chars(text, max_chars), total - max_chars))
18}
19
20#[cfg(test)]
21mod tests {
22    use super::*;
23
24    #[test]
25    fn test_truncate_chars() {
26        assert_eq!(truncate_chars("hello world", 5), "hello");
27        assert_eq!(truncate_chars("hello", 5), "hello");
28        assert_eq!(truncate_chars("hi", 5), "hi");
29        assert_eq!(truncate_chars("hello", 0), "");
30        assert_eq!(truncate_chars("", 5), "");
31        assert_eq!(truncate_chars("πŸ‘‹πŸŒŽ", 1), "πŸ‘‹");
32        assert_eq!(truncate_chars("πŸ‘‹πŸŒŽ", 2), "πŸ‘‹πŸŒŽ");
33        assert_eq!(truncate_chars("こんにけは", 2), "こん");
34        assert_eq!(truncate_chars("μ•ˆλ…•ν•˜μ„Έμš”", 3), "μ•ˆλ…•ν•˜");
35        assert_eq!(truncate_chars("δ½ ε₯½δΈ–η•Œ", 1), "δ½ ");
36    }
37
38    #[test]
39    fn test_truncate_chars_counted() {
40        assert_eq!(truncate_chars_counted("hello", 5), None);
41        assert_eq!(truncate_chars_counted("δ½ ε₯½δΈ–η•Œ", 4), None);
42        assert_eq!(
43            truncate_chars_counted("δ½ ε₯½δΈ–η•Œ", 2),
44            Some(("δ½ ε₯½".to_string(), 2))
45        );
46        let cjk = "ζ—₯".repeat(30_000);
47        let (text, dropped) = truncate_chars_counted(&cjk, 20_000).unwrap();
48        assert_eq!(text.chars().count(), 20_000);
49        assert_eq!(dropped, 10_000);
50    }
51}