1use heck::{ToPascalCase, ToSnakeCase};
4
5pub fn to_struct_name(table_name: &str) -> String {
7 table_name.to_pascal_case()
8}
9
10pub fn to_field_name(column_name: &str) -> String {
12 column_name.to_snake_case()
13}
14
15pub fn to_enum_name(table_name: &str, column_name: &str) -> String {
18 format!(
19 "{}{}",
20 table_name.to_pascal_case(),
21 column_name.to_pascal_case()
22 )
23}
24
25pub fn generate_find_by_method_name(columns: &[String]) -> String {
28 let parts: Vec<String> = columns.iter().map(|c| c.to_snake_case()).collect();
29 format!("find_by_{}", parts.join("_and_"))
30}
31
32pub fn generate_find_by_list_method_name(column: &str) -> String {
36 let snake = column.to_snake_case();
37 let plural = pluralize(&snake);
38 if plural == snake {
39 format!("find_by_{}_list", snake)
41 } else {
42 format!("find_by_{}", plural)
43 }
44}
45
46pub fn generate_delete_by_method_name(columns: &[String]) -> String {
48 let parts: Vec<String> = columns.iter().map(|c| c.to_snake_case()).collect();
49 format!("delete_by_{}", parts.join("_and_"))
50}
51
52pub fn generate_update_by_method_name(columns: &[String]) -> String {
54 let parts: Vec<String> = columns.iter().map(|c| c.to_snake_case()).collect();
55 format!("update_by_{}", parts.join("_and_"))
56}
57
58pub fn to_enum_variant(value: &str) -> String {
61 let value = value.trim_matches('\'').trim_matches('"');
63
64 value.to_pascal_case()
66}
67
68pub fn pluralize(word: &str) -> String {
70 if word.is_empty() {
71 return word.to_string();
72 }
73
74 let irregulars: &[(&str, &str)] = &[
76 ("person", "people"),
77 ("child", "children"),
78 ("man", "men"),
79 ("woman", "women"),
80 ("foot", "feet"),
81 ("tooth", "teeth"),
82 ("mouse", "mice"),
83 ("index", "indices"),
84 ];
85
86 for (singular, plural) in irregulars {
87 if word == *singular {
88 return plural.to_string();
89 }
90 }
91
92 if word.ends_with("is") && word.len() > 2 {
94 return format!("{}es", &word[..word.len() - 2]);
95 }
96
97 if let Some(stripped) = word.strip_suffix("fe") {
99 return format!("{}ves", stripped);
100 }
101 let f_to_ves: &[&str] = &[
102 "leaf", "knife", "wife", "life", "shelf", "self", "half", "calf", "loaf", "thief",
103 ];
104 for &fword in f_to_ves {
105 if word == fword {
106 return format!("{}ves", &word[..word.len() - 1]);
107 }
108 }
109
110 let o_to_oes: &[&str] = &["hero", "potato", "tomato", "echo", "veto"];
112 for &oword in o_to_oes {
113 if word == oword {
114 return format!("{}es", word);
115 }
116 }
117
118 if word.ends_with("ed") && word.len() > 2 {
121 return word.to_string();
122 }
123
124 if word.ends_with("s")
126 || word.ends_with("x")
127 || word.ends_with("z")
128 || word.ends_with("ch")
129 || word.ends_with("sh")
130 {
131 return format!("{}es", word);
132 }
133
134 if word.ends_with("y") && word.len() > 1 {
136 let before_y = word.chars().nth(word.len() - 2).unwrap_or('_');
137 if !"aeiou".contains(before_y) {
138 return format!("{}ies", &word[..word.len() - 1]);
139 }
140 }
141
142 format!("{}s", word)
144}
145
146pub fn is_rust_keyword(name: &str) -> bool {
148 matches!(
149 name,
150 "as" | "async"
151 | "await"
152 | "break"
153 | "const"
154 | "continue"
155 | "crate"
156 | "dyn"
157 | "else"
158 | "enum"
159 | "extern"
160 | "false"
161 | "fn"
162 | "for"
163 | "if"
164 | "impl"
165 | "in"
166 | "let"
167 | "loop"
168 | "match"
169 | "mod"
170 | "move"
171 | "mut"
172 | "pub"
173 | "ref"
174 | "return"
175 | "self"
176 | "Self"
177 | "static"
178 | "struct"
179 | "super"
180 | "trait"
181 | "true"
182 | "type"
183 | "unsafe"
184 | "use"
185 | "where"
186 | "while"
187 | "abstract"
188 | "become"
189 | "box"
190 | "do"
191 | "final"
192 | "macro"
193 | "override"
194 | "priv"
195 | "try"
196 | "typeof"
197 | "unsized"
198 | "virtual"
199 | "yield"
200 )
201}
202
203pub fn escape_field_name(name: &str) -> String {
205 let snake = name.to_snake_case();
206 if is_rust_keyword(&snake) {
207 format!("r#{}", snake)
208 } else {
209 snake
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_to_struct_name() {
219 assert_eq!(to_struct_name("users"), "Users");
220 assert_eq!(to_struct_name("user_settings"), "UserSettings");
221 assert_eq!(to_struct_name("order_items"), "OrderItems");
222 }
223
224 #[test]
225 fn test_to_field_name() {
226 assert_eq!(to_field_name("userId"), "user_id");
227 assert_eq!(to_field_name("first_name"), "first_name");
228 assert_eq!(to_field_name("CreatedAt"), "created_at");
229 }
230
231 #[test]
232 fn test_to_enum_name() {
233 assert_eq!(to_enum_name("users", "status"), "UsersStatus");
234 assert_eq!(
235 to_enum_name("order_items", "payment_type"),
236 "OrderItemsPaymentType"
237 );
238 }
239
240 #[test]
241 fn test_to_enum_variant() {
242 assert_eq!(to_enum_variant("ACTIVE"), "Active");
243 assert_eq!(to_enum_variant("'active'"), "Active");
244 assert_eq!(to_enum_variant("IN_PROGRESS"), "InProgress");
245 assert_eq!(to_enum_variant("PendingReview"), "PendingReview");
246 }
247
248 #[test]
249 fn test_pluralize() {
250 assert_eq!(pluralize("id"), "ids");
252 assert_eq!(pluralize("user"), "users");
253 assert_eq!(pluralize("email"), "emails");
254
255 assert_eq!(pluralize("status"), "statuses");
257 assert_eq!(pluralize("box"), "boxes");
258 assert_eq!(pluralize("match"), "matches");
259 assert_eq!(pluralize("dish"), "dishes");
260
261 assert_eq!(pluralize("category"), "categories");
263 assert_eq!(pluralize("company"), "companies");
264 assert_eq!(pluralize("key"), "keys");
266 assert_eq!(pluralize("day"), "days");
267
268 assert_eq!(pluralize("analysis"), "analyses");
270 assert_eq!(pluralize("basis"), "bases");
271
272 assert_eq!(pluralize("leaf"), "leaves");
274 assert_eq!(pluralize("knife"), "knives");
275
276 assert_eq!(pluralize("person"), "people");
278 assert_eq!(pluralize("child"), "children");
279 assert_eq!(pluralize("index"), "indices");
280
281 assert_eq!(pluralize("hero"), "heroes");
283 assert_eq!(pluralize("photo"), "photos");
284
285 assert_eq!(pluralize("published"), "published");
287 assert_eq!(pluralize("deleted"), "deleted");
288 assert_eq!(pluralize("updated"), "updated");
289 }
290
291 #[test]
292 fn test_generate_find_by_method_name() {
293 assert_eq!(
294 generate_find_by_method_name(&["id".to_string()]),
295 "find_by_id"
296 );
297 assert_eq!(
298 generate_find_by_method_name(&["user_id".to_string(), "device_type".to_string()]),
299 "find_by_user_id_and_device_type"
300 );
301 }
302
303 #[test]
304 fn test_escape_field_name() {
305 assert_eq!(escape_field_name("type"), "r#type");
306 assert_eq!(escape_field_name("name"), "name");
307 assert_eq!(escape_field_name("async"), "r#async");
308 }
309}