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)))); } }