apollo-agent 0.4.0

Local-first Rust AI agent runtime β€” Telegram-first, trait-driven, SurrealDB + RocksDB state layer.
Documentation
//! Small text utilities shared across tools and providers.

pub fn truncate_chars(text: &str, max_chars: usize) -> String {
    text.chars().take(max_chars).collect()
}

/// Truncate to `max_chars` characters.
///
/// Returns `None` when the text already fits, otherwise the truncated text
/// and the number of characters dropped. The gate and the truncation use the
/// same unit so the reported count is always accurate.
pub fn truncate_chars_counted(text: &str, max_chars: usize) -> Option<(String, usize)> {
    let total = text.chars().count();
    if total <= max_chars {
        return None;
    }
    Some((truncate_chars(text, max_chars), total - max_chars))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_truncate_chars() {
        assert_eq!(truncate_chars("hello world", 5), "hello");
        assert_eq!(truncate_chars("hello", 5), "hello");
        assert_eq!(truncate_chars("hi", 5), "hi");
        assert_eq!(truncate_chars("hello", 0), "");
        assert_eq!(truncate_chars("", 5), "");
        assert_eq!(truncate_chars("πŸ‘‹πŸŒŽ", 1), "πŸ‘‹");
        assert_eq!(truncate_chars("πŸ‘‹πŸŒŽ", 2), "πŸ‘‹πŸŒŽ");
        assert_eq!(truncate_chars("こんにけは", 2), "こん");
        assert_eq!(truncate_chars("μ•ˆλ…•ν•˜μ„Έμš”", 3), "μ•ˆλ…•ν•˜");
        assert_eq!(truncate_chars("δ½ ε₯½δΈ–η•Œ", 1), "δ½ ");
    }

    #[test]
    fn test_truncate_chars_counted() {
        assert_eq!(truncate_chars_counted("hello", 5), None);
        assert_eq!(truncate_chars_counted("δ½ ε₯½δΈ–η•Œ", 4), None);
        assert_eq!(
            truncate_chars_counted("δ½ ε₯½δΈ–η•Œ", 2),
            Some(("δ½ ε₯½".to_string(), 2))
        );
        let cjk = "ζ—₯".repeat(30_000);
        let (text, dropped) = truncate_chars_counted(&cjk, 20_000).unwrap();
        assert_eq!(text.chars().count(), 20_000);
        assert_eq!(dropped, 10_000);
    }
}