|
| 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