pub trait Operator { type FirstIter: Iterator; type RestIter: Iterator; type OpenSection: Iterator; fn first_iter(c: char) -> Self::FirstIter; fn open_section(c: char) -> Self::OpenSection; fn rest_iter(c: char) -> Self::RestIter; fn sep() -> &'static str; } pub trait Source { fn operate(&self, input: &str) -> String where O: Operator; } pub trait Naming { fn convert(&self, input: &str) -> String; /// Copy the given naming policy. fn copy(&self) -> Box; } #[derive(Clone, Copy)] pub struct ToLowerCamel(()); impl Naming for ToLowerCamel { fn convert(&self, input: &str) -> String { operate::(input) } fn copy(&self) -> Box { Box::new(*self) } } impl Operator for ToLowerCamel { type FirstIter = ::std::char::ToLowercase; type OpenSection = ::std::char::ToUppercase; type RestIter = ::std::char::ToLowercase; fn first_iter(c: char) -> Self::FirstIter { c.to_lowercase() } fn open_section(c: char) -> Self::OpenSection { c.to_uppercase() } fn rest_iter(c: char) -> Self::RestIter { c.to_lowercase() } fn sep() -> &'static str { "" } } #[derive(Clone, Copy)] pub struct ToUpperCamel(()); impl Naming for ToUpperCamel { fn convert(&self, input: &str) -> String { operate::(input) } fn copy(&self) -> Box { Box::new(*self) } } impl Operator for ToUpperCamel { type FirstIter = ::std::char::ToUppercase; type OpenSection = ::std::char::ToUppercase; type RestIter = ::std::char::ToLowercase; fn first_iter(c: char) -> Self::FirstIter { c.to_uppercase() } fn open_section(c: char) -> Self::OpenSection { c.to_uppercase() } fn rest_iter(c: char) -> Self::RestIter { c.to_lowercase() } fn sep() -> &'static str { "" } } #[derive(Clone, Copy)] pub struct ToLowerSnake(()); impl Naming for ToLowerSnake { fn convert(&self, input: &str) -> String { operate::(input) } fn copy(&self) -> Box { Box::new(*self) } } impl Operator for ToLowerSnake { type FirstIter = ::std::char::ToLowercase; type OpenSection = ::std::char::ToLowercase; type RestIter = ::std::char::ToLowercase; fn first_iter(c: char) -> Self::FirstIter { c.to_lowercase() } fn open_section(c: char) -> Self::OpenSection { c.to_lowercase() } fn rest_iter(c: char) -> Self::RestIter { c.to_lowercase() } fn sep() -> &'static str { "_" } } #[derive(Clone, Copy)] pub struct ToUpperSnake(()); impl Naming for ToUpperSnake { fn convert(&self, input: &str) -> String { operate::(input) } fn copy(&self) -> Box { Box::new(*self) } } impl Operator for ToUpperSnake { type FirstIter = ::std::char::ToUppercase; type OpenSection = ::std::char::ToUppercase; type RestIter = ::std::char::ToUppercase; fn first_iter(c: char) -> Self::FirstIter { c.to_uppercase() } fn open_section(c: char) -> Self::OpenSection { c.to_uppercase() } fn rest_iter(c: char) -> Self::RestIter { c.to_uppercase() } fn sep() -> &'static str { "_" } } #[derive(Clone)] pub struct AnyCase(()); impl AnyCase { pub fn new() -> AnyCase { AnyCase(()) } } /// A source for camel-cased strings. fn operate(input: &str) -> String where O: Operator, { let mut buf = String::new(); let mut it = input.chars(); let mut open_section = true; let mut first = true; while let Some(c) = it.next() { match c { '_' if first => buf.push('_'), '_' => open_section = true, c if first => { buf.extend(O::first_iter(c)); first = false; open_section = false; } c if c.is_uppercase() || open_section => { buf.push_str(O::sep()); buf.extend(O::open_section(c)); open_section = false; } c => { buf.extend(O::rest_iter(c)); } } } buf } pub fn to_lower_camel() -> ToLowerCamel { ToLowerCamel(()) } pub fn to_upper_camel() -> ToUpperCamel { ToUpperCamel(()) } pub fn to_lower_snake() -> ToLowerSnake { ToLowerSnake(()) } pub fn to_upper_snake() -> ToUpperSnake { ToUpperSnake(()) } #[cfg(test)] mod tests { use super::*; #[test] pub fn preserve_prefix() { let naming = to_lower_camel(); assert_eq!("_fooBarBaz", naming.convert("_FooBarBaz")); assert_eq!("_fooBarBaz", naming.convert("_fooBarBaz")); assert_eq!("_fooBarBaz", naming.convert("_foo_bar_baz")); assert_eq!("_fooBarBaz", naming.convert("_Foo_Bar_baz")); } #[test] pub fn lower_camel_test() { let naming = to_lower_camel(); assert_eq!("fooBarBaz", naming.convert("FooBarBaz")); assert_eq!("fooBarBaz", naming.convert("fooBarBaz")); assert_eq!("fooBarBaz", naming.convert("foo_bar_baz")); assert_eq!("fooBarBaz", naming.convert("Foo_Bar_baz")); } #[test] pub fn upper_camel_test() { let naming = to_upper_camel(); assert_eq!("FooBarBaz", naming.convert("FooBarBaz")); assert_eq!("FooBarBaz", naming.convert("fooBarBaz")); assert_eq!("FooBBarBaz", naming.convert("fooBBarBaz")); assert_eq!("FooBarBaz", naming.convert("foo_bar_baz")); assert_eq!("FooBarBaz", naming.convert("foo_Bar_baz")); } #[test] pub fn lower_snake_test() { let naming = to_lower_snake(); assert_eq!("foo_bar_baz", naming.convert("FooBarBaz")); assert_eq!("foo_bar_baz", naming.convert("fooBarBaz")); } #[test] pub fn upper_snake_test() { let naming = to_upper_snake(); assert_eq!("FOO_BAR_BAZ", naming.convert("FooBarBaz")); assert_eq!("FOO_BAR_BAZ", naming.convert("fooBarBaz")); assert_eq!("FOO_BAR_BAZ", naming.convert("foo_bar_baz")); } }