/// Returns the index of the character after the first camel-case component of `s`.
#[must_use]
pub fn until(s: &str) -> usize {
    let mut iter = s.char_indices();
    if let Some((_, first)) = iter.next() {
        if !first.is_uppercase() {
            return 0;
        }
    } else {
        return 0;
    }
    let mut up = true;
    let mut last_i = 0;
    for (i, c) in iter {
        if up {
            if c.is_lowercase() {
                up = false;
            } else {
                return last_i;
            }
        } else if c.is_uppercase() {
            up = true;
            last_i = i;
        } else if !c.is_lowercase() {
            return i;
        }
    }
    if up {
        last_i
    } else {
        s.len()
    }
}

/// Returns index of the last camel-case component of `s`.
#[must_use]
pub fn from(s: &str) -> usize {
    let mut iter = s.char_indices().rev();
    if let Some((_, first)) = iter.next() {
        if !first.is_lowercase() {
            return s.len();
        }
    } else {
        return s.len();
    }
    let mut down = true;
    let mut last_i = s.len();
    for (i, c) in iter {
        if down {
            if c.is_uppercase() {
                down = false;
                last_i = i;
            } else if !c.is_lowercase() {
                return last_i;
            }
        } else if c.is_lowercase() {
            down = true;
        } else {
            return last_i;
        }
    }
    last_i
}

#[cfg(test)]
mod test {
    use super::{from, until};

    #[test]
    fn from_full() {
        assert_eq!(from("AbcDef"), 0);
        assert_eq!(from("Abc"), 0);
    }

    #[test]
    fn from_partial() {
        assert_eq!(from("abcDef"), 3);
        assert_eq!(from("aDbc"), 1);
    }

    #[test]
    fn from_not() {
        assert_eq!(from("AbcDef_"), 7);
        assert_eq!(from("AbcDD"), 5);
    }

    #[test]
    fn from_caps() {
        assert_eq!(from("ABCD"), 4);
    }

    #[test]
    fn until_full() {
        assert_eq!(until("AbcDef"), 6);
        assert_eq!(until("Abc"), 3);
    }

    #[test]
    fn until_not() {
        assert_eq!(until("abcDef"), 0);
        assert_eq!(until("aDbc"), 0);
    }

    #[test]
    fn until_partial() {
        assert_eq!(until("AbcDef_"), 6);
        assert_eq!(until("CallTypeC"), 8);
        assert_eq!(until("AbcDD"), 3);
    }

    #[test]
    fn until_caps() {
        assert_eq!(until("ABCD"), 0);
    }
}
