pub mod file_utils;
pub mod project_structure_formatter;
pub mod threads;
pub mod token_estimator;
pub mod prompt_compressor;
pub fn floor_char_boundary(s: &str, max_bytes: usize) -> usize {
if s.len() <= max_bytes {
return s.len();
}
let mut i = max_bytes;
while i > 0 && !s.is_char_boundary(i) {
i -= 1;
}
i
}
pub fn ceil_char_boundary(s: &str, min_bytes: usize) -> usize {
if min_bytes >= s.len() {
return s.len();
}
let mut i = min_bytes;
while i < s.len() && !s.is_char_boundary(i) {
i += 1;
}
i
}
pub fn truncate_at_char_boundary(s: &str, max_bytes: usize) -> &str {
&s[..floor_char_boundary(s, max_bytes)]
}
pub fn slice_from_char_boundary(s: &str, from_bytes: usize) -> &str {
&s[ceil_char_boundary(s, from_bytes)..]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_ascii_unchanged() {
assert_eq!(truncate_at_char_boundary("abcdef", 3), "abc");
}
#[test]
fn test_truncate_multibyte_floors_to_boundary() {
let s = "中文中文";
assert_eq!(s.len(), 12);
assert_eq!(truncate_at_char_boundary(s, 4), "中");
assert_eq!(truncate_at_char_boundary(s, 12), "中文中文");
assert_eq!(truncate_at_char_boundary(s, 100), "中文中文");
}
#[test]
fn test_slice_from_multibyte_ceils_to_boundary() {
let s = "中文中文";
assert_eq!(slice_from_char_boundary(s, 4), "中文");
assert_eq!(slice_from_char_boundary(s, 3), "文中文");
assert_eq!(slice_from_char_boundary(s, 0), s);
assert_eq!(slice_from_char_boundary(s, 12), "");
assert_eq!(slice_from_char_boundary(s, 100), "");
}
#[test]
fn test_truncate_never_panics_on_any_offset() {
let s = "aé中😀b";
for n in 0..=s.len() + 2 {
let _ = truncate_at_char_boundary(s, n);
let _ = slice_from_char_boundary(s, n);
}
}
}