1pub trait CaseExt {
2 type Owned;
3
4 fn to_snake(&self) -> Self::Owned;
13
14 fn to_camel(&self) -> Self::Owned;
21}
22
23impl CaseExt for str {
24 type Owned = String;
25
26 fn to_snake(&self) -> Self::Owned {
27 let mut s = String::new();
28 let mut upper = true;
29 let mut upper_count = 0;
30
31 for c in self.chars() {
32 let next_upper = if c.is_uppercase() {
33 true
34 } else if c.is_lowercase() {
35 false
36 } else {
37 upper
38 };
39
40 if !upper && next_upper {
41 s.push('_');
42 } else if upper && !next_upper && upper_count >= 3 {
43 let n = s.len() - s.chars().next_back().unwrap().len_utf8();
44 s.insert(n, '_');
45 }
46
47 s.extend(c.to_lowercase());
48
49 if next_upper {
50 upper_count += 1;
51 upper = true;
52 } else {
53 upper_count = 0;
54 upper = false;
55 }
56 }
57 s
58 }
59
60 fn to_camel(&self) -> Self::Owned {
61 let new_length = self.chars().filter(|c| *c != '_').count();
62 let mut s = String::with_capacity(new_length);
63 let mut under = true;
64 for c in self.chars() {
65 if under && c.is_lowercase() {
66 s.extend(c.to_uppercase());
67 } else if c != '_' {
68 s.push(c);
69 }
70 under = c == '_';
71 }
72 s
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn to_snake() {
82 let cases = [
83 ("AtkGObjectAccessible", "atk_gobject_accessible"),
84 ("AtkNoOpObject", "atk_no_op_object"),
85 ("DConfClient", "dconf_client"),
86 ("GCabCabinet", "gcab_cabinet"),
87 ("GstA52Dec", "gst_a52_dec"),
88 ("GstLameMP3Enc", "gst_lame_mp3_enc"),
89 ("GstMpg123AudioDec", "gst_mpg123_audio_dec"),
90 ("GstX264Enc", "gst_x264_enc"),
91 ("FileIOStream", "file_io_stream"),
92 ("IOStream", "io_stream"),
93 ("IMContext", "im_context"),
94 ("DBusMessage", "dbus_message"),
95 ("SoupCookieJarDB", "soup_cookie_jar_db"),
96 ("FooBarBaz", "foo_bar_baz"),
97 ("aBcDe", "a_bc_de"),
98 ("aXXbYYc", "a_xxb_yyc"),
99 ];
100
101 for &(input, expected) in &cases {
102 assert_eq!(expected, input.to_snake());
103 }
104 }
105
106 #[test]
107 fn to_camel() {
108 assert_eq!("foo_bar_baz".to_camel(), "FooBarBaz");
109 assert_eq!("_foo".to_camel(), "Foo");
110 }
111}