1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
//! Pacakage inflect provides utility functions to inflect strings.
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

use std::ascii::AsciiExt;

pub trait StringExt {
    fn to_upper_camel(&self) -> String;
    fn to_lower_camel(&self) -> String;
}

impl<'a> StringExt for &'a str {
    fn to_upper_camel(&self) -> String {
        UpperCamel::convert_to(self)
    }
    fn to_lower_camel(&self) -> String {
        LowerCamel::convert_to(self)
    }
}

pub trait CaseFormat {
    fn convert_to(src: &str) -> String;
}

/// Case Format
/// e.g) "CaseFormat", "UpperCamel"
pub struct UpperCamel;

impl CaseFormat for UpperCamel {
    fn convert_to(src: &str) -> String {
        to_camel_case(src, true)
    }
}

/// Case Format
/// e.g) "caseFormat", "upperCamel"
pub struct LowerCamel;

impl CaseFormat for LowerCamel {
    fn convert_to(src: &str) -> String {
        to_camel_case(src, false)
    }
}


#[inline]
fn to_camel_case(src: &str, first_upper: bool) -> String {
    if src.len() == 0 {
        return String::new();
    }

    let mut string = String::with_capacity(src.len());
    let mut chars = src.chars();
    // safe
    if first_upper {
        string.push(chars.next().unwrap().to_ascii_uppercase());
    } else {
        string.push(chars.next().unwrap().to_ascii_lowercase());
    }

    'outer: while let Some(ch) = chars.next() {
        match ch {
            '-' | '_' | ' ' => {
                if let Some(n) = chars.next() {
                    string.push(n.to_ascii_uppercase());
                } else {
                    // if string ends with this character, just skip it.
                    break 'outer;
                }
            }
            _ => string.push(ch),
        }
    }

    string
}

#[cfg(test)]
mod tests {
    pub use super::*;

    #[test]
    fn to_upper_camel() {
        let convert_to = UpperCamel::convert_to;
        assert_eq!("Foo", convert_to("foo"));
        assert_eq!("FooBar", convert_to("foo-bar"));
        assert_eq!("FooBar", convert_to("foo bar"));
        assert_eq!("FooBar", convert_to("foo_bar"));
        assert_eq!("FooBar", convert_to("fooBar"));
    }

    #[test]
    fn to_lower_camel() {
        let convert_to = LowerCamel::convert_to;
        assert_eq!("foo", convert_to("Foo"));
        assert_eq!("fooBar", convert_to("foo-bar"));
        assert_eq!("fooBar", convert_to("foo bar"));
        assert_eq!("fooBar", convert_to("foo_bar"));
        assert_eq!("fooBar", convert_to("FooBar"));
    }
}