vwi_core/
unicode_utils.rs1use unicode_normalization::UnicodeNormalization;
2
3pub fn normalize_nfc(text: &str) -> String {
5 text.nfc().collect()
6}
7
8pub struct CharOffsetMap {
10 byte_to_char: Vec<usize>,
11 total_chars: usize,
12}
13
14impl CharOffsetMap {
15 pub fn new(text: &str) -> Self {
16 let mut byte_to_char = Vec::with_capacity(text.len() + 1);
17 let mut char_count: usize = 0;
18
19 for (byte_idx, _) in text.char_indices() {
20 while byte_to_char.len() < byte_idx {
21 byte_to_char.push(char_count.saturating_sub(1));
22 }
23 byte_to_char.push(char_count);
24 char_count += 1;
25 }
26
27 while byte_to_char.len() <= text.len() {
28 byte_to_char.push(char_count);
29 }
30
31 Self {
32 byte_to_char,
33 total_chars: char_count,
34 }
35 }
36
37 #[inline]
39 pub fn byte_to_char(&self, byte_offset: usize) -> usize {
40 if byte_offset >= self.byte_to_char.len() {
41 self.total_chars
42 } else {
43 self.byte_to_char[byte_offset]
44 }
45 }
46
47 #[inline]
49 pub fn convert_range(&self, start_byte: usize, end_byte: usize) -> (usize, usize) {
50 (self.byte_to_char(start_byte), self.byte_to_char(end_byte))
51 }
52
53 #[inline]
55 pub fn byte_range_to_char_range(&self, start_byte: usize, end_byte: usize) -> (usize, usize) {
56 self.convert_range(start_byte, end_byte)
57 }
58}
59
60#[inline]
62pub fn is_word_boundary(text: &str, start_byte: usize, end_byte: usize) -> bool {
63 let bytes = text.as_bytes();
64 if start_byte > 0 {
65 if let Some(prev_char) = text[..start_byte].chars().next_back() {
66 if prev_char.is_alphanumeric() || prev_char == '_' || prev_char == '-' {
67 return false;
68 }
69 }
70 }
71 if end_byte < bytes.len() {
72 if let Some(next_char) = text[end_byte..].chars().next() {
73 if next_char.is_alphanumeric() || next_char == '_' || next_char == '-' {
74 return false;
75 }
76 }
77 }
78 true
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_char_offset_mapping_vietnamese() {
87 let text = "Trí tuệ nhân tạo";
88 let mapper = CharOffsetMap::new(text);
89
90 assert_eq!(mapper.byte_to_char(0), 0);
93 assert_eq!(mapper.byte_to_char(4), 3);
94 assert_eq!(mapper.byte_to_char(5), 4); assert_eq!(mapper.byte_to_char(text.len()), 16);
98 }
99}