|
| 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)] |
| 4 | +pub struct StrIndex { |
| 5 | + pub char_index: usize, |
| 6 | + pub byte_index: usize, |
| 7 | +} |
| 8 | + |
| 9 | +impl StrIndex { |
| 10 | + pub fn new(char_index: usize, byte_index: usize) -> Self { |
| 11 | + Self { char_index, byte_index } |
| 12 | + } |
| 13 | +} |
| 14 | + |
1 | 15 | /// Returns the index of the character after the first camel-case component of `s`. |
| 16 | +/// |
| 17 | +/// ``` |
| 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)); |
| 22 | +/// ``` |
2 | 23 | #[must_use] |
3 | | -pub fn until(s: &str) -> usize { |
4 | | - let mut iter = s.char_indices(); |
5 | | - if let Some((_, first)) = iter.next() { |
| 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() { |
6 | 27 | if !first.is_uppercase() { |
7 | | - return 0; |
| 28 | + return StrIndex::new(0, 0); |
8 | 29 | } |
9 | 30 | } else { |
10 | | - return 0; |
| 31 | + return StrIndex::new(0, 0); |
11 | 32 | } |
12 | 33 | let mut up = true; |
13 | | - let mut last_i = 0; |
14 | | - for (i, c) in iter { |
| 34 | + let mut last_index = StrIndex::new(0, 0); |
| 35 | + for (char_index, (byte_index, c)) in iter { |
15 | 36 | if up { |
16 | 37 | if c.is_lowercase() { |
17 | 38 | up = false; |
18 | 39 | } else { |
19 | | - return last_i; |
| 40 | + return last_index; |
20 | 41 | } |
21 | 42 | } else if c.is_uppercase() { |
22 | 43 | up = true; |
23 | | - last_i = i; |
| 44 | + last_index.byte_index = byte_index; |
| 45 | + last_index.char_index = char_index; |
24 | 46 | } else if !c.is_lowercase() { |
25 | | - return i; |
| 47 | + return StrIndex::new(char_index, byte_index); |
26 | 48 | } |
27 | 49 | } |
28 | | - if up { last_i } else { s.len() } |
| 50 | + |
| 51 | + if up { |
| 52 | + last_index |
| 53 | + } else { |
| 54 | + StrIndex::new(s.chars().count(), s.len()) |
| 55 | + } |
29 | 56 | } |
30 | 57 |
|
31 | 58 | /// Returns index of the last camel-case component of `s`. |
| 59 | +/// |
| 60 | +/// ``` |
| 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)); |
| 66 | +/// ``` |
32 | 67 | #[must_use] |
33 | | -pub fn from(s: &str) -> usize { |
34 | | - let mut iter = s.char_indices().rev(); |
35 | | - if let Some((_, first)) = iter.next() { |
| 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() { |
36 | 73 | if !first.is_lowercase() { |
37 | | - return s.len(); |
| 74 | + return StrIndex::new(char_index, s.len()); |
38 | 75 | } |
39 | 76 | } else { |
40 | | - return s.len(); |
| 77 | + return StrIndex::new(char_count, s.len()); |
41 | 78 | } |
42 | 79 | let mut down = true; |
43 | | - let mut last_i = s.len(); |
44 | | - for (i, c) in iter { |
| 80 | + let mut last_index = StrIndex::new(char_count, s.len()); |
| 81 | + for (char_index, (byte_index, c)) in iter { |
45 | 82 | if down { |
46 | 83 | if c.is_uppercase() { |
47 | 84 | down = false; |
48 | | - last_i = i; |
| 85 | + last_index.byte_index = byte_index; |
| 86 | + last_index.char_index = char_index; |
49 | 87 | } else if !c.is_lowercase() { |
50 | | - return last_i; |
| 88 | + return last_index; |
51 | 89 | } |
52 | 90 | } else if c.is_lowercase() { |
53 | 91 | down = true; |
54 | 92 | } else if c.is_uppercase() { |
55 | | - last_i = i; |
| 93 | + last_index.byte_index = byte_index; |
| 94 | + last_index.char_index = char_index; |
56 | 95 | } else { |
57 | | - return last_i; |
| 96 | + return last_index; |
58 | 97 | } |
59 | 98 | } |
60 | | - last_i |
| 99 | + last_index |
61 | 100 | } |
62 | 101 |
|
63 | 102 | #[cfg(test)] |
64 | 103 | mod test { |
65 | | - use super::{from, until}; |
| 104 | + use super::*; |
66 | 105 |
|
67 | 106 | #[test] |
68 | | - fn from_full() { |
69 | | - assert_eq!(from("AbcDef"), 0); |
70 | | - assert_eq!(from("Abc"), 0); |
71 | | - assert_eq!(from("ABcd"), 0); |
72 | | - assert_eq!(from("ABcdEf"), 0); |
73 | | - assert_eq!(from("AabABcd"), 0); |
| 107 | + fn camel_case_start_full() { |
| 108 | + assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0)); |
| 109 | + assert_eq!(camel_case_start("Abc"), StrIndex::new(0, 0)); |
| 110 | + assert_eq!(camel_case_start("ABcd"), StrIndex::new(0, 0)); |
| 111 | + assert_eq!(camel_case_start("ABcdEf"), StrIndex::new(0, 0)); |
| 112 | + assert_eq!(camel_case_start("AabABcd"), StrIndex::new(0, 0)); |
74 | 113 | } |
75 | 114 |
|
76 | 115 | #[test] |
77 | | - fn from_partial() { |
78 | | - assert_eq!(from("abcDef"), 3); |
79 | | - assert_eq!(from("aDbc"), 1); |
80 | | - assert_eq!(from("aabABcd"), 3); |
| 116 | + fn camel_case_start_partial() { |
| 117 | + assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3)); |
| 118 | + assert_eq!(camel_case_start("aDbc"), StrIndex::new(1, 1)); |
| 119 | + assert_eq!(camel_case_start("aabABcd"), StrIndex::new(3, 3)); |
| 120 | + assert_eq!(camel_case_start("\u{f6}\u{f6}AabABcd"), StrIndex::new(2, 4)); |
81 | 121 | } |
82 | 122 |
|
83 | 123 | #[test] |
84 | | - fn from_not() { |
85 | | - assert_eq!(from("AbcDef_"), 7); |
86 | | - assert_eq!(from("AbcDD"), 5); |
| 124 | + fn camel_case_start_not() { |
| 125 | + assert_eq!(camel_case_start("AbcDef_"), StrIndex::new(7, 7)); |
| 126 | + assert_eq!(camel_case_start("AbcDD"), StrIndex::new(5, 5)); |
| 127 | + assert_eq!(camel_case_start("all_small"), StrIndex::new(9, 9)); |
| 128 | + assert_eq!(camel_case_start("\u{f6}_all_small"), StrIndex::new(11, 12)); |
87 | 129 | } |
88 | 130 |
|
89 | 131 | #[test] |
90 | | - fn from_caps() { |
91 | | - assert_eq!(from("ABCD"), 4); |
| 132 | + fn camel_case_start_caps() { |
| 133 | + assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4)); |
92 | 134 | } |
93 | 135 |
|
94 | 136 | #[test] |
95 | | - fn until_full() { |
96 | | - assert_eq!(until("AbcDef"), 6); |
97 | | - assert_eq!(until("Abc"), 3); |
| 137 | + fn camel_case_until_full() { |
| 138 | + assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6)); |
| 139 | + assert_eq!(camel_case_until("Abc"), StrIndex::new(3, 3)); |
| 140 | + assert_eq!(camel_case_until("Abc\u{f6}\u{f6}\u{f6}"), StrIndex::new(6, 9)); |
98 | 141 | } |
99 | 142 |
|
100 | 143 | #[test] |
101 | | - fn until_not() { |
102 | | - assert_eq!(until("abcDef"), 0); |
103 | | - assert_eq!(until("aDbc"), 0); |
| 144 | + fn camel_case_until_not() { |
| 145 | + assert_eq!(camel_case_until("abcDef"), StrIndex::new(0, 0)); |
| 146 | + assert_eq!(camel_case_until("aDbc"), StrIndex::new(0, 0)); |
104 | 147 | } |
105 | 148 |
|
106 | 149 | #[test] |
107 | | - fn until_partial() { |
108 | | - assert_eq!(until("AbcDef_"), 6); |
109 | | - assert_eq!(until("CallTypeC"), 8); |
110 | | - assert_eq!(until("AbcDD"), 3); |
| 150 | + fn camel_case_until_partial() { |
| 151 | + assert_eq!(camel_case_until("AbcDef_"), StrIndex::new(6, 6)); |
| 152 | + assert_eq!(camel_case_until("CallTypeC"), StrIndex::new(8, 8)); |
| 153 | + assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3)); |
| 154 | + assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7)); |
111 | 155 | } |
112 | 156 |
|
113 | 157 | #[test] |
114 | 158 | fn until_caps() { |
115 | | - assert_eq!(until("ABCD"), 0); |
| 159 | + assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0)); |
116 | 160 | } |
117 | 161 | } |
0 commit comments