1 /// Dealing with sting indices can be hard, this struct ensures that both the
2 /// character and byte index are provided for correct indexing.
3 #[derive(Debug, Default, PartialEq, Eq)]
10 pub fn new(char_index: usize, byte_index: usize) -> Self {
11 Self { char_index, byte_index }
15 /// Returns the index of the character after the first camel-case component of `s`.
18 /// assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
19 /// assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
20 /// assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3));
21 /// assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7));
24 pub fn camel_case_until(s: &str) -> StrIndex {
25 let mut iter = s.char_indices().enumerate();
26 if let Some((_char_index, (_, first))) = iter.next() {
27 if !first.is_uppercase() {
28 return StrIndex::new(0, 0);
31 return StrIndex::new(0, 0);
34 let mut last_index = StrIndex::new(0, 0);
35 for (char_index, (byte_index, c)) in iter {
42 } else if c.is_uppercase() {
44 last_index.byte_index = byte_index;
45 last_index.char_index = char_index;
46 } else if !c.is_lowercase() {
47 return StrIndex::new(char_index, byte_index);
54 StrIndex::new(s.chars().count(), s.len())
58 /// Returns index of the last camel-case component of `s`.
61 /// assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
62 /// assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
63 /// assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4));
64 /// assert_eq!(camel_case_start("abcd"), StrIndex::new(4, 4));
65 /// assert_eq!(camel_case_start("\u{f6}\u{f6}cd"), StrIndex::new(4, 6));
68 pub fn camel_case_start(s: &str) -> StrIndex {
69 let char_count = s.chars().count();
70 let range = 0..char_count;
71 let mut iter = range.rev().zip(s.char_indices().rev());
72 if let Some((char_index, (_, first))) = iter.next() {
73 if !first.is_lowercase() {
74 return StrIndex::new(char_index, s.len());
77 return StrIndex::new(char_count, s.len());
80 let mut last_index = StrIndex::new(char_count, s.len());
81 for (char_index, (byte_index, c)) in iter {
85 last_index.byte_index = byte_index;
86 last_index.char_index = char_index;
87 } else if !c.is_lowercase() {
90 } else if c.is_lowercase() {
92 } else if c.is_uppercase() {
93 last_index.byte_index = byte_index;
94 last_index.char_index = char_index;
102 /// Dealing with sting comparison can be complicated, this struct ensures that both the
103 /// character and byte count are provided for correct indexing.
104 #[derive(Debug, Default, PartialEq, Eq)]
105 pub struct StrCount {
106 pub char_count: usize,
107 pub byte_count: usize,
111 pub fn new(char_count: usize, byte_count: usize) -> Self {
112 Self { char_count, byte_count }
116 /// Returns the number of chars that match from the start
119 /// assert_eq!(count_match_start("hello_mouse", "hello_penguin"), StrCount::new(6, 6));
120 /// assert_eq!(count_match_start("hello_clippy", "bye_bugs"), StrCount::new(0, 0));
121 /// assert_eq!(count_match_start("hello_world", "hello_world"), StrCount::new(11, 11));
122 /// assert_eq!(count_match_start("T\u{f6}ffT\u{f6}ff", "T\u{f6}ff"), StrCount::new(4, 5));
125 pub fn count_match_start(str1: &str, str2: &str) -> StrCount {
126 // (char_index, char1)
127 let char_count = str1.chars().count();
128 let iter1 = (0..=char_count).zip(str1.chars());
129 // (byte_index, char2)
130 let iter2 = str2.char_indices();
134 .take_while(|((_, c1), (_, c2))| c1 == c2)
136 .map_or_else(StrCount::default, |((char_index, _), (byte_index, character))| {
137 StrCount::new(char_index + 1, byte_index + character.len_utf8())
141 /// Returns the number of chars and bytes that match from the end
144 /// assert_eq!(count_match_end("hello_cat", "bye_cat"), StrCount::new(4, 4));
145 /// assert_eq!(count_match_end("if_item_thing", "enum_value"), StrCount::new(0, 0));
146 /// assert_eq!(count_match_end("Clippy", "Clippy"), StrCount::new(6, 6));
147 /// assert_eq!(count_match_end("MyT\u{f6}ff", "YourT\u{f6}ff"), StrCount::new(4, 5));
150 pub fn count_match_end(str1: &str, str2: &str) -> StrCount {
151 let char_count = str1.chars().count();
153 return StrCount::default();
156 // (char_index, char1)
157 let iter1 = (0..char_count).rev().zip(str1.chars().rev());
158 // (byte_index, char2)
159 let byte_count = str2.len();
160 let iter2 = str2.char_indices().rev();
164 .take_while(|((_, c1), (_, c2))| c1 == c2)
166 .map_or_else(StrCount::default, |((char_index, _), (byte_index, _))| {
167 StrCount::new(char_count - char_index, byte_count - byte_index)
176 fn camel_case_start_full() {
177 assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
178 assert_eq!(camel_case_start("Abc"), StrIndex::new(0, 0));
179 assert_eq!(camel_case_start("ABcd"), StrIndex::new(0, 0));
180 assert_eq!(camel_case_start("ABcdEf"), StrIndex::new(0, 0));
181 assert_eq!(camel_case_start("AabABcd"), StrIndex::new(0, 0));
185 fn camel_case_start_partial() {
186 assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
187 assert_eq!(camel_case_start("aDbc"), StrIndex::new(1, 1));
188 assert_eq!(camel_case_start("aabABcd"), StrIndex::new(3, 3));
189 assert_eq!(camel_case_start("\u{f6}\u{f6}AabABcd"), StrIndex::new(2, 4));
193 fn camel_case_start_not() {
194 assert_eq!(camel_case_start("AbcDef_"), StrIndex::new(7, 7));
195 assert_eq!(camel_case_start("AbcDD"), StrIndex::new(5, 5));
196 assert_eq!(camel_case_start("all_small"), StrIndex::new(9, 9));
197 assert_eq!(camel_case_start("\u{f6}_all_small"), StrIndex::new(11, 12));
201 fn camel_case_start_caps() {
202 assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4));
206 fn camel_case_until_full() {
207 assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
208 assert_eq!(camel_case_until("Abc"), StrIndex::new(3, 3));
209 assert_eq!(camel_case_until("Abc\u{f6}\u{f6}\u{f6}"), StrIndex::new(6, 9));
213 fn camel_case_until_not() {
214 assert_eq!(camel_case_until("abcDef"), StrIndex::new(0, 0));
215 assert_eq!(camel_case_until("aDbc"), StrIndex::new(0, 0));
219 fn camel_case_until_partial() {
220 assert_eq!(camel_case_until("AbcDef_"), StrIndex::new(6, 6));
221 assert_eq!(camel_case_until("CallTypeC"), StrIndex::new(8, 8));
222 assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3));
223 assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7));
228 assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));