1 //! Utility functions for working with identifiers.
2 
3 use heck::{CamelCase, SnakeCase};
4 
5 /// Converts a `camelCase` or `SCREAMING_SNAKE_CASE` identifier to a `lower_snake` case Rust field
6 /// identifier.
to_snake(s: &str) -> String7 pub fn to_snake(s: &str) -> String {
8     let mut ident = s.to_snake_case();
9 
10     // Use a raw identifier if the identifier matches a Rust keyword:
11     // https://doc.rust-lang.org/reference/keywords.html.
12     match ident.as_str() {
13         // 2015 strict keywords.
14         | "as" | "break" | "const" | "continue" | "else" | "enum" | "false"
15         | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut"
16         | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "true"
17         | "type" | "unsafe" | "use" | "where" | "while"
18         // 2018 strict keywords.
19         | "dyn"
20         // 2015 reserved keywords.
21         | "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" | "priv" | "typeof"
22         | "unsized" | "virtual" | "yield"
23         // 2018 reserved keywords.
24         | "async" | "await" | "try" => ident.insert_str(0, "r#"),
25         // the following keywords are not supported as raw identifiers and are therefore suffixed with an underscore.
26         "self" | "super" | "extern" | "crate" => ident += "_",
27         _ => (),
28     }
29     ident
30 }
31 
32 /// Converts a `snake_case` identifier to an `UpperCamel` case Rust type identifier.
to_upper_camel(s: &str) -> String33 pub fn to_upper_camel(s: &str) -> String {
34     let mut ident = s.to_camel_case();
35 
36     // Suffix an underscore for the `Self` Rust keyword as it is not allowed as raw identifier.
37     if ident == "Self" {
38         ident += "_";
39     }
40     ident
41 }
42 
43 #[cfg(test)]
44 mod tests {
45 
46     #![allow(clippy::cognitive_complexity)]
47 
48     use super::*;
49 
50     #[test]
test_to_snake()51     fn test_to_snake() {
52         assert_eq!("foo_bar", &to_snake("FooBar"));
53         assert_eq!("foo_bar_baz", &to_snake("FooBarBAZ"));
54         assert_eq!("foo_bar_baz", &to_snake("FooBarBAZ"));
55         assert_eq!("xml_http_request", &to_snake("XMLHttpRequest"));
56         assert_eq!("r#while", &to_snake("While"));
57         assert_eq!("fuzz_buster", &to_snake("FUZZ_BUSTER"));
58         assert_eq!("foo_bar_baz", &to_snake("foo_bar_baz"));
59         assert_eq!("fuzz_buster", &to_snake("FUZZ_buster"));
60         assert_eq!("fuzz", &to_snake("_FUZZ"));
61         assert_eq!("fuzz", &to_snake("_fuzz"));
62         assert_eq!("fuzz", &to_snake("_Fuzz"));
63         assert_eq!("fuzz", &to_snake("FUZZ_"));
64         assert_eq!("fuzz", &to_snake("fuzz_"));
65         assert_eq!("fuzz", &to_snake("Fuzz_"));
66         assert_eq!("fuz_z", &to_snake("FuzZ_"));
67 
68         // From test_messages_proto3.proto.
69         assert_eq!("fieldname1", &to_snake("fieldname1"));
70         assert_eq!("field_name2", &to_snake("field_name2"));
71         assert_eq!("field_name3", &to_snake("_field_name3"));
72         assert_eq!("field_name4", &to_snake("field__name4_"));
73         assert_eq!("field0name5", &to_snake("field0name5"));
74         assert_eq!("field_0_name6", &to_snake("field_0_name6"));
75         assert_eq!("field_name7", &to_snake("fieldName7"));
76         assert_eq!("field_name8", &to_snake("FieldName8"));
77         assert_eq!("field_name9", &to_snake("field_Name9"));
78         assert_eq!("field_name10", &to_snake("Field_Name10"));
79 
80         // TODO(withoutboats/heck#3)
81         //assert_eq!("field_name11", &to_snake("FIELD_NAME11"));
82         assert_eq!("field_name12", &to_snake("FIELD_name12"));
83         assert_eq!("field_name13", &to_snake("__field_name13"));
84         assert_eq!("field_name14", &to_snake("__Field_name14"));
85         assert_eq!("field_name15", &to_snake("field__name15"));
86         assert_eq!("field_name16", &to_snake("field__Name16"));
87         assert_eq!("field_name17", &to_snake("field_name17__"));
88         assert_eq!("field_name18", &to_snake("Field_name18__"));
89     }
90 
91     #[test]
test_to_snake_raw_keyword()92     fn test_to_snake_raw_keyword() {
93         assert_eq!("r#as", &to_snake("as"));
94         assert_eq!("r#break", &to_snake("break"));
95         assert_eq!("r#const", &to_snake("const"));
96         assert_eq!("r#continue", &to_snake("continue"));
97         assert_eq!("r#else", &to_snake("else"));
98         assert_eq!("r#enum", &to_snake("enum"));
99         assert_eq!("r#false", &to_snake("false"));
100         assert_eq!("r#fn", &to_snake("fn"));
101         assert_eq!("r#for", &to_snake("for"));
102         assert_eq!("r#if", &to_snake("if"));
103         assert_eq!("r#impl", &to_snake("impl"));
104         assert_eq!("r#in", &to_snake("in"));
105         assert_eq!("r#let", &to_snake("let"));
106         assert_eq!("r#loop", &to_snake("loop"));
107         assert_eq!("r#match", &to_snake("match"));
108         assert_eq!("r#mod", &to_snake("mod"));
109         assert_eq!("r#move", &to_snake("move"));
110         assert_eq!("r#mut", &to_snake("mut"));
111         assert_eq!("r#pub", &to_snake("pub"));
112         assert_eq!("r#ref", &to_snake("ref"));
113         assert_eq!("r#return", &to_snake("return"));
114         assert_eq!("r#static", &to_snake("static"));
115         assert_eq!("r#struct", &to_snake("struct"));
116         assert_eq!("r#trait", &to_snake("trait"));
117         assert_eq!("r#true", &to_snake("true"));
118         assert_eq!("r#type", &to_snake("type"));
119         assert_eq!("r#unsafe", &to_snake("unsafe"));
120         assert_eq!("r#use", &to_snake("use"));
121         assert_eq!("r#where", &to_snake("where"));
122         assert_eq!("r#while", &to_snake("while"));
123         assert_eq!("r#dyn", &to_snake("dyn"));
124         assert_eq!("r#abstract", &to_snake("abstract"));
125         assert_eq!("r#become", &to_snake("become"));
126         assert_eq!("r#box", &to_snake("box"));
127         assert_eq!("r#do", &to_snake("do"));
128         assert_eq!("r#final", &to_snake("final"));
129         assert_eq!("r#macro", &to_snake("macro"));
130         assert_eq!("r#override", &to_snake("override"));
131         assert_eq!("r#priv", &to_snake("priv"));
132         assert_eq!("r#typeof", &to_snake("typeof"));
133         assert_eq!("r#unsized", &to_snake("unsized"));
134         assert_eq!("r#virtual", &to_snake("virtual"));
135         assert_eq!("r#yield", &to_snake("yield"));
136         assert_eq!("r#async", &to_snake("async"));
137         assert_eq!("r#await", &to_snake("await"));
138         assert_eq!("r#try", &to_snake("try"));
139     }
140 
141     #[test]
test_to_snake_non_raw_keyword()142     fn test_to_snake_non_raw_keyword() {
143         assert_eq!("self_", &to_snake("self"));
144         assert_eq!("super_", &to_snake("super"));
145         assert_eq!("extern_", &to_snake("extern"));
146         assert_eq!("crate_", &to_snake("crate"));
147     }
148 
149     #[test]
test_to_upper_camel()150     fn test_to_upper_camel() {
151         assert_eq!("", &to_upper_camel(""));
152         assert_eq!("F", &to_upper_camel("F"));
153         assert_eq!("Foo", &to_upper_camel("FOO"));
154         assert_eq!("FooBar", &to_upper_camel("FOO_BAR"));
155         assert_eq!("FooBar", &to_upper_camel("_FOO_BAR"));
156         assert_eq!("FooBar", &to_upper_camel("FOO_BAR_"));
157         assert_eq!("FooBar", &to_upper_camel("_FOO_BAR_"));
158         assert_eq!("FuzzBuster", &to_upper_camel("fuzzBuster"));
159         assert_eq!("FuzzBuster", &to_upper_camel("FuzzBuster"));
160         assert_eq!("Self_", &to_upper_camel("self"));
161     }
162 }
163