@@ -68,6 +68,16 @@ pub fn camel_case_until(s: &str) -> StrIndex {
6868/// ```
6969#[ must_use]
7070pub fn camel_case_start ( s : & str ) -> StrIndex {
71+ camel_case_start_from_idx ( s, 0 )
72+ }
73+
74+ /// Returns `StrIndex` of the last camel-case component of `s[idx..]`.
75+ ///
76+ /// ```
77+ /// assert_eq!(camel_case_start("AbcDef", 0), StrIndex::new(0, 0));
78+ /// assert_eq!(camel_case_start("AbcDef", 1), StrIndex::new(3, 3));
79+ /// ```
80+ pub fn camel_case_start_from_idx ( s : & str , start_idx : usize ) -> StrIndex {
7181 let char_count = s. chars ( ) . count ( ) ;
7282 let range = 0 ..char_count;
7383 let mut iter = range. rev ( ) . zip ( s. char_indices ( ) . rev ( ) ) ;
@@ -78,9 +88,13 @@ pub fn camel_case_start(s: &str) -> StrIndex {
7888 } else {
7989 return StrIndex :: new ( char_count, s. len ( ) ) ;
8090 }
91+
8192 let mut down = true ;
8293 let mut last_index = StrIndex :: new ( char_count, s. len ( ) ) ;
8394 for ( char_index, ( byte_index, c) ) in iter {
95+ if byte_index < start_idx {
96+ continue ;
97+ }
8498 if down {
8599 if c. is_uppercase ( ) {
86100 down = false ;
@@ -98,9 +112,51 @@ pub fn camel_case_start(s: &str) -> StrIndex {
98112 return last_index;
99113 }
100114 }
115+
101116 last_index
102117}
103118
119+ /// Get the indexes of camel case components of a string `s`
120+ ///
121+ /// ```
122+ /// assert_eq!(camel_case_indexes("AbcDef"), vec![StrIndex::new(0, 0), StrIndex::new(3, 3)])
123+ /// ```
124+ pub fn camel_case_indexes ( s : & str ) -> Vec < StrIndex > {
125+ let mut result = Vec :: new ( ) ;
126+ let mut str_idx = camel_case_start ( s) ;
127+
128+ while str_idx. byte_index < s. len ( ) {
129+ let next_idx = str_idx. byte_index + 1 ;
130+ result. push ( str_idx) ;
131+ str_idx = camel_case_start_from_idx ( s, next_idx) ;
132+ }
133+ result. push ( str_idx) ;
134+
135+ result
136+ }
137+
138+ /// Split camel case string into a vector of its components
139+ ///
140+ /// ```
141+ /// assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]);
142+ /// ```
143+ pub fn camel_case_split ( s : & str ) -> Vec < & str > {
144+ let offsets = camel_case_indexes ( s) ;
145+ let mut idxs_iter = offsets. iter ( ) . map ( |str_idx| str_idx. byte_index ) . peekable ( ) ;
146+ let idxs: Vec < usize > = if let Some ( & idx) = idxs_iter. peek ( ) {
147+ if idx == 0 {
148+ idxs_iter. collect ( )
149+ } else {
150+ Vec :: < usize > :: from ( [ 0 ] ) . into_iter ( ) . chain ( idxs_iter) . collect ( )
151+ }
152+ } else {
153+ return vec ! [ s] ;
154+ } ;
155+ let split_points: Vec < ( & usize , & usize ) > = idxs[ ..idxs. len ( ) - 1 ] . iter ( ) . zip ( & idxs[ 1 ..] ) . collect ( ) ;
156+
157+ split_points. iter ( ) . map ( |( & start, & stop) | & s[ start..stop] ) . collect ( )
158+ }
159+
104160/// Dealing with sting comparison can be complicated, this struct ensures that both the
105161/// character and byte count are provided for correct indexing.
106162#[ derive( Debug , Default , PartialEq , Eq ) ]
@@ -231,4 +287,29 @@ mod test {
231287 fn until_caps ( ) {
232288 assert_eq ! ( camel_case_until( "ABCD" ) , StrIndex :: new( 0 , 0 ) ) ;
233289 }
290+
291+ #[ test]
292+ fn camel_case_indexes_full ( ) {
293+ assert_eq ! (
294+ camel_case_indexes( "AbcDef" ) ,
295+ vec![ StrIndex :: new( 0 , 0 ) , StrIndex :: new( 3 , 3 ) ]
296+ ) ;
297+ assert_eq ! (
298+ camel_case_indexes( "abcDef" ) ,
299+ vec![ StrIndex :: new( 0 , 0 ) , StrIndex :: new( 3 , 3 ) ]
300+ ) ;
301+ assert_eq ! ( camel_case_indexes( "Abc\u{f6} \u{f6} DD" ) , vec![ StrIndex :: new( 5 , 7 ) ] ) ;
302+ }
303+
304+ #[ test]
305+ fn camel_case_split_full ( ) {
306+ assert_eq ! ( camel_case_split( "A" ) , vec![ "A" ] ) ;
307+ assert_eq ! ( camel_case_split( "AbcDef" ) , vec![ "Abc" , "Def" ] ) ;
308+ assert_eq ! ( camel_case_split( "Abc" ) , vec![ "Abc" ] ) ;
309+ assert_eq ! ( camel_case_split( "abcDef" ) , vec![ "abc" , "Def" ] ) ;
310+ assert_eq ! (
311+ camel_case_split( "\u{f6} \u{f6} AabABcd" ) ,
312+ vec![ "\u{f6} \u{f6} " , "Aab" , "A" , "Bcd" ]
313+ ) ;
314+ }
234315}
0 commit comments