2021-10-24 14:06:17 -05:00
|
|
|
/// Dealing with sting indices can be hard, this struct ensures that both the
|
|
|
|
/// character and byte index are provided for correct indexing.
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq)]
|
|
|
|
pub struct StrIndex {
|
|
|
|
pub char_index: usize,
|
|
|
|
pub byte_index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StrIndex {
|
|
|
|
pub fn new(char_index: usize, byte_index: usize) -> Self {
|
|
|
|
Self { char_index, byte_index }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:19:47 -05:00
|
|
|
/// Returns the index of the character after the first camel-case component of `s`.
|
2021-10-24 14:06:17 -05:00
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
|
|
|
|
/// assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
|
|
|
|
/// assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3));
|
|
|
|
/// assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7));
|
|
|
|
/// ```
|
2019-09-18 01:37:41 -05:00
|
|
|
#[must_use]
|
2021-10-24 14:06:17 -05:00
|
|
|
pub fn camel_case_until(s: &str) -> StrIndex {
|
|
|
|
let mut iter = s.char_indices().enumerate();
|
|
|
|
if let Some((_char_index, (_, first))) = iter.next() {
|
2018-08-28 21:32:20 -05:00
|
|
|
if !first.is_uppercase() {
|
2021-10-24 14:06:17 -05:00
|
|
|
return StrIndex::new(0, 0);
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
} else {
|
2021-10-24 14:06:17 -05:00
|
|
|
return StrIndex::new(0, 0);
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
let mut up = true;
|
2021-10-24 14:06:17 -05:00
|
|
|
let mut last_index = StrIndex::new(0, 0);
|
|
|
|
for (char_index, (byte_index, c)) in iter {
|
2018-08-28 21:32:20 -05:00
|
|
|
if up {
|
|
|
|
if c.is_lowercase() {
|
|
|
|
up = false;
|
|
|
|
} else {
|
2021-10-24 14:06:17 -05:00
|
|
|
return last_index;
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
} else if c.is_uppercase() {
|
|
|
|
up = true;
|
2021-10-24 14:06:17 -05:00
|
|
|
last_index.byte_index = byte_index;
|
|
|
|
last_index.char_index = char_index;
|
2018-08-28 21:32:20 -05:00
|
|
|
} else if !c.is_lowercase() {
|
2021-10-24 14:06:17 -05:00
|
|
|
return StrIndex::new(char_index, byte_index);
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
}
|
2021-10-24 14:06:17 -05:00
|
|
|
|
|
|
|
if up {
|
|
|
|
last_index
|
|
|
|
} else {
|
|
|
|
StrIndex::new(s.chars().count(), s.len())
|
|
|
|
}
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
2019-01-30 19:15:29 -06:00
|
|
|
/// Returns index of the last camel-case component of `s`.
|
2021-10-24 14:06:17 -05:00
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
|
|
|
|
/// assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
|
|
|
|
/// assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4));
|
|
|
|
/// assert_eq!(camel_case_start("abcd"), StrIndex::new(4, 4));
|
|
|
|
/// assert_eq!(camel_case_start("\u{f6}\u{f6}cd"), StrIndex::new(4, 6));
|
|
|
|
/// ```
|
2019-09-18 01:37:41 -05:00
|
|
|
#[must_use]
|
2021-10-24 14:06:17 -05:00
|
|
|
pub fn camel_case_start(s: &str) -> StrIndex {
|
|
|
|
let char_count = s.chars().count();
|
|
|
|
let range = 0..char_count;
|
|
|
|
let mut iter = range.rev().zip(s.char_indices().rev());
|
|
|
|
if let Some((char_index, (_, first))) = iter.next() {
|
2018-08-28 21:32:20 -05:00
|
|
|
if !first.is_lowercase() {
|
2021-10-24 14:06:17 -05:00
|
|
|
return StrIndex::new(char_index, s.len());
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
} else {
|
2021-10-24 14:06:17 -05:00
|
|
|
return StrIndex::new(char_count, s.len());
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
let mut down = true;
|
2021-10-24 14:06:17 -05:00
|
|
|
let mut last_index = StrIndex::new(char_count, s.len());
|
|
|
|
for (char_index, (byte_index, c)) in iter {
|
2018-08-28 21:32:20 -05:00
|
|
|
if down {
|
|
|
|
if c.is_uppercase() {
|
|
|
|
down = false;
|
2021-10-24 14:06:17 -05:00
|
|
|
last_index.byte_index = byte_index;
|
|
|
|
last_index.char_index = char_index;
|
2018-08-28 21:32:20 -05:00
|
|
|
} else if !c.is_lowercase() {
|
2021-10-24 14:06:17 -05:00
|
|
|
return last_index;
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
} else if c.is_lowercase() {
|
|
|
|
down = true;
|
2021-02-20 12:48:04 -06:00
|
|
|
} else if c.is_uppercase() {
|
2021-10-24 14:06:17 -05:00
|
|
|
last_index.byte_index = byte_index;
|
|
|
|
last_index.char_index = char_index;
|
2018-08-28 21:32:20 -05:00
|
|
|
} else {
|
2021-10-24 14:06:17 -05:00
|
|
|
return last_index;
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
}
|
2021-10-24 14:06:17 -05:00
|
|
|
last_index
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2021-10-24 14:06:17 -05:00
|
|
|
use super::*;
|
2018-08-28 21:32:20 -05:00
|
|
|
|
|
|
|
#[test]
|
2021-10-24 14:06:17 -05:00
|
|
|
fn camel_case_start_full() {
|
|
|
|
assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
|
|
|
|
assert_eq!(camel_case_start("Abc"), StrIndex::new(0, 0));
|
|
|
|
assert_eq!(camel_case_start("ABcd"), StrIndex::new(0, 0));
|
|
|
|
assert_eq!(camel_case_start("ABcdEf"), StrIndex::new(0, 0));
|
|
|
|
assert_eq!(camel_case_start("AabABcd"), StrIndex::new(0, 0));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-10-24 14:06:17 -05:00
|
|
|
fn camel_case_start_partial() {
|
|
|
|
assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
|
|
|
|
assert_eq!(camel_case_start("aDbc"), StrIndex::new(1, 1));
|
|
|
|
assert_eq!(camel_case_start("aabABcd"), StrIndex::new(3, 3));
|
|
|
|
assert_eq!(camel_case_start("\u{f6}\u{f6}AabABcd"), StrIndex::new(2, 4));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-10-24 14:06:17 -05:00
|
|
|
fn camel_case_start_not() {
|
|
|
|
assert_eq!(camel_case_start("AbcDef_"), StrIndex::new(7, 7));
|
|
|
|
assert_eq!(camel_case_start("AbcDD"), StrIndex::new(5, 5));
|
|
|
|
assert_eq!(camel_case_start("all_small"), StrIndex::new(9, 9));
|
|
|
|
assert_eq!(camel_case_start("\u{f6}_all_small"), StrIndex::new(11, 12));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-10-24 14:06:17 -05:00
|
|
|
fn camel_case_start_caps() {
|
|
|
|
assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-10-24 14:06:17 -05:00
|
|
|
fn camel_case_until_full() {
|
|
|
|
assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
|
|
|
|
assert_eq!(camel_case_until("Abc"), StrIndex::new(3, 3));
|
|
|
|
assert_eq!(camel_case_until("Abc\u{f6}\u{f6}\u{f6}"), StrIndex::new(6, 9));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-10-24 14:06:17 -05:00
|
|
|
fn camel_case_until_not() {
|
|
|
|
assert_eq!(camel_case_until("abcDef"), StrIndex::new(0, 0));
|
|
|
|
assert_eq!(camel_case_until("aDbc"), StrIndex::new(0, 0));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-10-24 14:06:17 -05:00
|
|
|
fn camel_case_until_partial() {
|
|
|
|
assert_eq!(camel_case_until("AbcDef_"), StrIndex::new(6, 6));
|
|
|
|
assert_eq!(camel_case_until("CallTypeC"), StrIndex::new(8, 8));
|
|
|
|
assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3));
|
|
|
|
assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn until_caps() {
|
2021-10-24 14:06:17 -05:00
|
|
|
assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
|
2018-08-28 21:32:20 -05:00
|
|
|
}
|
2018-10-11 17:36:40 -05:00
|
|
|
}
|