krabka_protocol_codegen/
name_conv.rs1#[must_use]
9pub fn field_name(s: &str) -> String {
10 let mut out = String::with_capacity(s.len() + 2);
11 let bytes = s.as_bytes();
12 for (i, &b) in bytes.iter().enumerate() {
13 if b.is_ascii_uppercase() {
14 let is_first = i == 0;
15 let prev_upper = i
16 .checked_sub(1)
17 .and_then(|prev| bytes.get(prev))
18 .is_some_and(u8::is_ascii_uppercase);
19 let next_lower = bytes.get(i + 1).is_some_and(u8::is_ascii_lowercase);
20 if !is_first && (!prev_upper || next_lower) {
21 out.push('_');
22 }
23 out.push(b.to_ascii_lowercase() as char);
24 } else {
25 out.push(b as char);
26 }
27 }
28 if is_reserved_keyword(&out) {
29 out.push('_');
30 }
31 out
32}
33
34#[must_use]
36pub fn module_name(s: &str) -> String {
37 field_name(s)
38}
39
40#[must_use]
44pub fn type_name(s: &str) -> String {
45 s.to_owned()
46}
47
48fn is_reserved_keyword(s: &str) -> bool {
49 matches!(
50 s,
51 "as" | "break"
52 | "const"
53 | "continue"
54 | "crate"
55 | "else"
56 | "enum"
57 | "extern"
58 | "false"
59 | "fn"
60 | "for"
61 | "if"
62 | "impl"
63 | "in"
64 | "let"
65 | "loop"
66 | "match"
67 | "mod"
68 | "move"
69 | "mut"
70 | "pub"
71 | "ref"
72 | "return"
73 | "self"
74 | "Self"
75 | "static"
76 | "struct"
77 | "super"
78 | "trait"
79 | "true"
80 | "type"
81 | "unsafe"
82 | "use"
83 | "where"
84 | "while"
85 | "async"
86 | "await"
87 | "dyn"
88 | "abstract"
89 | "become"
90 | "box"
91 | "do"
92 | "final"
93 | "macro"
94 | "override"
95 | "priv"
96 | "typeof"
97 | "unsized"
98 | "virtual"
99 | "yield"
100 | "try"
101 | "union"
102 | "gen"
103 )
104}
105
106#[cfg(test)]
107mod tests {
108 use assert2::assert;
109
110 use super::*;
111
112 #[test]
113 fn camel_to_snake() {
114 for (input, want) in [
115 ("errorCode", "error_code"),
116 ("apiKeys", "api_keys"),
117 ("aclEntries", "acl_entries"),
118 ("zkMigrationReady", "zk_migration_ready"),
119 ] {
120 assert!(field_name(input) == want);
121 }
122 }
123
124 #[test]
125 fn pascal_to_snake() {
126 assert!(field_name("ZkMigrationReady") == "zk_migration_ready");
127 assert!(field_name("ApiVersionsRequest") == "api_versions_request");
128 }
129
130 #[test]
131 fn acronym_runs_stay_together() {
132 assert!(field_name("KafkaClusterID") == "kafka_cluster_id");
134 assert!(field_name("HTTPSEndpoint") == "https_endpoint");
136 }
137
138 #[test]
139 fn reserved_keywords_get_underscore() {
140 for (input, want) in [("type", "type_"), ("Match", "match_"), ("loop", "loop_")] {
141 assert!(field_name(input) == want);
142 }
143 }
144
145 #[test]
146 fn module_name_uses_snake_case() {
147 assert!(module_name("ApiVersionsRequest") == "api_versions_request");
148 assert!(module_name("OffsetCommitResponse") == "offset_commit_response");
149 }
150}