| 215 | |
| 216 | impl TextSplitterTrait for CharacterSplitter { |
| 217 | fn split_text(&self, text: &str) -> GraphBitResult<Vec<TextChunk>> { |
| 218 | if text.is_empty() { |
| 219 | return Ok(Vec::new()); |
| 220 | } |
| 221 | |
| 222 | let mut chunks = Vec::new(); |
| 223 | let mut chunk_index = 0; |
| 224 | |
| 225 | // Build a mapping of character index to byte index |
| 226 | let char_to_byte: Vec<(usize, usize)> = text |
| 227 | .char_indices() |
| 228 | .enumerate() |
| 229 | .map(|(char_idx, (byte_idx, _))| (char_idx, byte_idx)) |
| 230 | .collect(); |
| 231 | |
| 232 | // Add the end position |
| 233 | let total_chars = text.chars().count(); |
| 234 | let mut char_to_byte_map = char_to_byte; |
| 235 | char_to_byte_map.push((total_chars, text.len())); |
| 236 | |
| 237 | let mut char_start = 0; |
| 238 | |
| 239 | while char_start < total_chars { |
| 240 | let mut char_end = (char_start + self.chunk_size).min(total_chars); |
| 241 | |
| 242 | // Get byte positions |
| 243 | let mut byte_start = char_to_byte_map[char_start].1; |
| 244 | let mut byte_end = char_to_byte_map[char_end].1; |
| 245 | |
| 246 | // Preserve word boundaries if enabled |
| 247 | if self.config.preserve_word_boundaries && char_end < total_chars { |
| 248 | // Look for the last whitespace in the chunk |
| 249 | let chunk_text = &text[byte_start..byte_end]; |
| 250 | if let Some(last_space_pos) = chunk_text.rfind(char::is_whitespace) { |
| 251 | // Adjust to the position after the whitespace |
| 252 | byte_end = byte_start |
| 253 | + last_space_pos |
| 254 | + chunk_text[last_space_pos..] |
| 255 | .chars() |
| 256 | .next() |
| 257 | .unwrap() |
| 258 | .len_utf8(); |
| 259 | |
| 260 | // Find the corresponding character position |
| 261 | for i in (char_start..char_end).rev() { |
| 262 | if char_to_byte_map[i + 1].1 == byte_end { |
| 263 | char_end = i + 1; |
| 264 | break; |
| 265 | } |
| 266 | } |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | let chunk_content = &text[byte_start..byte_end]; |
| 271 | let content = if self.config.trim_whitespace { |
| 272 | chunk_content.trim() |
| 273 | } else { |
| 274 | chunk_content |