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
102
103
104
105
106
#![no_std]

#![feature(unicode)]

extern crate either;
extern crate std_unicode;

#[cfg(test)]
#[macro_use]
extern crate std;

use core::iter::{Filter, FlatMap, Once, once};
use either::Either;
use std_unicode::char::{ToLowercase, ToUppercase};

use self::Either::{Left, Right};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Case {
    Camel(bool),
    Snake(bool),
}

use self::Case::*;

#[derive(Clone, Debug)]
pub struct Convert<Xs>(Either<FlatMap<Filter<ToCamelCase<Xs>, fn(&(Option<bool>, char)) -> bool>,
                                      Either<Once<char>, ToUppercase>,
                                      fn((Option<bool>, char)) -> Either<Once<char>, ToUppercase>>,
                              FlatMap<ToSnakeCase<Xs>,
                                      Either<ToLowercase, ToUppercase>,
                                      fn(char) -> Either<ToLowercase, ToUppercase>>>);

impl<Xs: Iterator<Item = char>> Convert<Xs> {
    #[inline]
    pub fn new(xs: Xs, c: Case) -> Self {
        Convert(match c {
            Camel(u) => Left (ToCamelCase { xs, last_x_opt: None }
                                  .filter({ fn f(&(_, x): &(Option<bool>, char)) -> bool { '_' != x }; f as _ })
                                  .flat_map(if u { (|(v, x): (Option<bool>, char)|
                                                    if v.unwrap_or(true)  { Right(x.to_uppercase()) }
                                                    else { Left(once(x)) }) as _ }
                                            else { (|(v, x): (Option<bool>, char)|
                                                    if v.unwrap_or(false) { Right(x.to_uppercase()) }
                                                    else { Left(once(x)) }) as _ })),
            Snake(u) => Right(ToSnakeCase { xs, x_opt: Right(false) }.flat_map(if u { (|x: char| Right(x.to_uppercase())) as _ }
                                                                               else { (|x: char| Left (x.to_lowercase())) as _ })),
        })
    }
}

impl<Xs: Iterator<Item = char>> Iterator for Convert<Xs> {
    type Item = char;

    #[inline]
    fn next(&mut self) -> Option<char> { self.0.next() }
}

#[derive(Clone, Debug)]
struct ToSnakeCase<Xs> {
    xs: Xs,
    x_opt: Either<char, bool>,
}

impl<Xs: Iterator<Item = char>> Iterator for ToSnakeCase<Xs> {
    type Item = char;

    #[inline]
    fn next(&mut self) -> Option<char> {
        match self.x_opt {
            Left(x) => { self.x_opt = Right(true); Some(x) },
            Right(b) => self.xs.next().map(|x| if x.is_uppercase() && b { self.x_opt = Left(x); '_' }
                                               else { self.x_opt = Right(x.is_lowercase()); x }),
        }
    }
}

#[derive(Clone, Debug)]
struct ToCamelCase<Xs> {
    xs: Xs,
    last_x_opt: Option<char>,
}

impl<Xs: Iterator<Item = char>> Iterator for ToCamelCase<Xs> {
    type Item = (Option<bool>, char);

    #[inline]
    fn next(&mut self) -> Option<(Option<bool>, char)> {
        let last_x_opt = self.last_x_opt;
        self.last_x_opt = self.xs.next();
        self.last_x_opt.map(|x| (last_x_opt.map(|x| '_' == x), x))
    }
}

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

    #[test]
    fn test() {
        assert!(Iterator::eq("camelCase".chars(), Convert::new("camel_case".chars(), Camel(false))));
        assert!(Iterator::eq("CamelCase".chars(), Convert::new("camel_case".chars(), Camel(true))));
        assert!(Iterator::eq("snake_case".chars(), Convert::new("SnakeCase".chars(), Snake(false))));
        assert!(Iterator::eq("SNAKE_CASE".chars(), Convert::new("SnakeCase".chars(), Snake(true))));
    }
}