prisma_rust_schema/
transform.rs1use crate::dmmf::Field;
2
3#[derive(Debug, Clone, PartialEq)]
4pub(crate) enum Case {
5 Snake,
6 Camel,
7 Pascal,
8 Kebab,
9 ScreamingSnake,
10 Unknown,
11}
12
13pub(crate) fn identify_case(input: &str) -> Case {
14 if input.is_empty() {
15 return Case::Snake;
16 }
17
18 let chars = input.chars().collect::<Vec<_>>();
19
20 if chars.iter().all(|c| c.is_uppercase() || c.is_numeric()) {
21 return Case::ScreamingSnake;
22 }
23
24 if chars.contains(&'_') {
25 if chars
26 .iter()
27 .all(|c| c.is_lowercase() || c.is_numeric() || *c == '_')
28 {
29 return Case::Snake;
30 }
31
32 if chars
33 .iter()
34 .all(|c| c.is_uppercase() || c.is_numeric() || *c == '_')
35 {
36 return Case::ScreamingSnake;
37 }
38
39 return Case::Unknown;
40 }
41
42 if chars.contains(&'-') {
43 if chars
44 .iter()
45 .all(|c| c.is_lowercase() || c.is_numeric() || *c == '-')
46 {
47 return Case::Kebab;
48 }
49
50 return Case::Unknown;
51 }
52
53 if chars.iter().any(|c| c.is_uppercase()) && !chars.contains(&'_') {
54 if chars[0].is_uppercase() {
55 return Case::Pascal;
56 }
57 return Case::Camel;
58 }
59
60 if chars.iter().all(|c| c.is_lowercase() || c.is_numeric()) {
61 return Case::Snake;
62 }
63
64 Case::Unknown
65}
66
67pub fn to_snake_case(input: &str) -> String {
71 let case = identify_case(input);
72
73 match case {
74 Case::Snake => input.to_string(),
75 Case::Camel => {
76 let mut result = String::new();
77 let mut capitalize_next = false;
78
79 for c in input.chars() {
80 if c.is_uppercase() {
81 if !result.is_empty() && !capitalize_next {
82 result.push('_');
83 }
84 result.push(c.to_ascii_lowercase());
85 capitalize_next = false;
86 } else {
87 result.push(c);
88 }
89 }
90
91 result
92 }
93 Case::Pascal => {
94 let mut result = String::new();
95 let mut capitalize_next = true;
96
97 for c in input.chars() {
98 if c.is_uppercase() {
99 if !result.is_empty() && !capitalize_next {
100 result.push('_');
101 }
102 result.push(c.to_ascii_lowercase());
103 capitalize_next = false;
104 } else {
105 result.push(c);
106 }
107 }
108
109 result
110 }
111 Case::Kebab => input.replace('-', "_").to_ascii_lowercase(),
112 Case::ScreamingSnake => input.to_ascii_lowercase(),
113 Case::Unknown => {
114 let mut result = String::new();
115 let mut capitalize_next = false;
116
117 for c in input.chars() {
118 if c.is_uppercase() {
119 if !result.is_empty() && !capitalize_next {
120 result.push('_');
121 }
122 result.push(c.to_ascii_lowercase());
123 capitalize_next = false;
124 } else {
125 result.push(c);
126 }
127 }
128
129 result
130 }
131 }
132}
133
134pub fn to_pascal_case(input: &str) -> String {
135 let case = identify_case(input);
136
137 match case {
138 Case::Snake => {
139 let mut result = String::new();
140 let mut capitalize_next = true;
141
142 for c in input.chars() {
143 if c == '_' {
144 capitalize_next = true;
145 } else if capitalize_next {
146 result.push(c.to_ascii_uppercase());
147 capitalize_next = false;
148 } else {
149 result.push(c);
150 }
151 }
152
153 result
154 }
155 Case::Camel => {
156 let mut result = input.get(0..1).unwrap_or_default().to_ascii_uppercase();
157 let mut capitalize_next = true;
158
159 for c in input.chars().skip(1) {
160 if c.is_uppercase() {
161 if !result.is_empty() && !capitalize_next {
162 result.push('_');
163 }
164 result.push(c.to_ascii_uppercase());
165 capitalize_next = false;
166 } else {
167 result.push(c);
168 }
169 }
170
171 result
172 }
173 Case::Pascal => input.to_string(),
174 Case::Kebab => {
175 let mut result = String::new();
176 let mut capitalize_next = true;
177
178 for c in input.chars() {
179 if c == '-' {
180 capitalize_next = true;
181 } else if capitalize_next {
182 result.push(c.to_ascii_uppercase());
183 capitalize_next = false;
184 } else {
185 result.push(c);
186 }
187 }
188
189 result
190 }
191 Case::ScreamingSnake => {
192 let mut result = String::new();
194 let mut capitalize_next = true;
195 for c in input.chars() {
196 if c == '_' {
197 capitalize_next = true;
198 } else if capitalize_next {
199 result.push(c.to_ascii_uppercase());
200 capitalize_next = false;
201 } else {
202 result.push(c.to_ascii_lowercase());
203 }
204 }
205 result
206 }
207 Case::Unknown => input.to_string(),
208 }
209}
210
211pub fn convert_field_to_type(field: &Field) -> String {
212 if let Some(native_type) = &field.native_type {
213 let t = native_type
215 .get(0)
216 .expect("Native type should have at least one part");
217 let t = t
218 .as_str()
219 .expect("Native type should be a stringified version");
220 match t {
222 "ObjectId" => "bson::oid::ObjectId".to_string(),
223 _ => unimplemented!("Unsupported native type: {}", t),
224 }
225 } else {
226 let scalar = match field.field_type.as_str() {
227 "Boolean" => "bool".to_string(),
228 "Int" => "i32".to_string(),
229 "Float" => "f32".to_string(),
230 "String" => "String".to_string(),
231 "Json" => "serde_json::Value".to_string(),
232 "DateTime" => "chrono::DateTime<chrono::Utc>".to_string(),
233 _ => to_pascal_case(&field.field_type),
234 };
235
236 let maybe_list = if field.is_list {
237 format!("Vec<{}>", scalar)
238 } else {
239 scalar
240 };
241
242 let maybe_option = if field.is_required {
243 maybe_list
244 } else {
245 format!("Option<{}>", maybe_list)
246 };
247
248 maybe_option
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn test_identify_case() {
258 assert_eq!(identify_case("hello_world"), Case::Snake);
259 assert_eq!(identify_case("HelloWorld"), Case::Pascal);
260 assert_eq!(identify_case("helloWorld"), Case::Camel);
261 assert_eq!(identify_case("hello-world"), Case::Kebab);
262 assert_eq!(identify_case("HELLO_WORLD"), Case::ScreamingSnake);
263 assert_eq!(identify_case("helloworld"), Case::Snake);
264 assert_eq!(identify_case("hello_world_123"), Case::Snake);
265 assert_eq!(identify_case("Hello_World"), Case::Unknown);
266 }
267
268 #[test]
269 fn test_to_snake_case() {
270 assert_eq!(to_snake_case("HelloWorld"), "hello_world");
271 assert_eq!(to_snake_case("helloWorld"), "hello_world");
272 assert_eq!(to_snake_case("hello_world"), "hello_world");
273 assert_eq!(to_snake_case("HELLO_WORLD"), "hello_world");
274 assert_eq!(to_snake_case("helloworld"), "helloworld");
275 }
276
277 #[test]
286 fn test_to_pascal_case() {
287 assert_eq!(to_pascal_case("hello_world"), "HelloWorld");
288 assert_eq!(to_pascal_case("helloWorld"), "HelloWorld");
289 assert_eq!(to_pascal_case("hello-world"), "HelloWorld");
290 assert_eq!(to_pascal_case("HELLO_WORLD"), "HelloWorld");
291 assert_eq!(to_pascal_case("helloworld"), "Helloworld");
292 assert_eq!(to_pascal_case("HelloWorld"), "HelloWorld");
293 }
294
295 }