1use crate::{models::{Model, RequestModel, ResponseModel}, Result};
2
3const RUST_RESERVED_KEYWORDS: &[&str] = &[
4 "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn",
5 "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref",
6 "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe",
7 "use", "where", "while",
8
9 "abstract", "become", "box", "do", "final", "macro", "override", "priv", "try",
10 "typeof", "unsized", "virtual", "yield",
11];
12
13const EMPTY_RESPONSE_NAME: &str = "UnknownResponse";
14const EMPTY_REQUEST_NAME: &str = "UnknownRequest";
15
16fn is_reserved_word(string_to_check: &str) -> bool {
17 RUST_RESERVED_KEYWORDS.contains(&string_to_check)
18}
19
20pub fn generate_models(
21 models: &[Model],
22 requests: &[RequestModel],
23 responses: &[ResponseModel],
24) -> Result<String> {
25 let mut output = String::new();
26
27 output.push_str("use serde::{Serialize, Deserialize};\n");
28 output.push_str("use uuid::Uuid;\n");
29 output.push_str("use chrono::{DateTime, NaiveDate, Utc};\n\n");
30
31 for model in models {
32 output.push_str(&generate_model(model)?);
33 output.push('\n');
34 }
35
36 for request in requests {
37 output.push_str(&generate_request_model(request)?);
38 output.push('\n');
39 }
40
41 for response in responses {
42 output.push_str(&generate_response_model(response)?);
43 output.push('\n');
44 }
45
46 Ok(output)
47}
48
49fn generate_model(model: &Model) -> Result<String> {
50 let mut output = String::new();
51
52 if !model.name.is_empty() {
53 output.push_str(&format!("/// {}\n", model.name));
54 }
55
56 output.push_str(&format!("#[derive(Debug, Serialize, Deserialize)]\n"));
57 output.push_str(&format!("pub struct {} {{\n", model.name));
58
59 for field in &model.fields {
60 let field_type = match field.field_type.as_str() {
61 "String" => "String",
62 "f64" => "f64",
63 "i64" => "i64",
64 "bool" => "bool",
65 "DateTime" => "DateTime<Utc>",
66 "Date" => "NaiveDate",
67 "Uuid" => "Uuid",
68 _ => &field.field_type,
69 };
70
71 let mut lowercased_name = field.name.to_lowercase();
72 if is_reserved_word(&lowercased_name) {
73 lowercased_name = format!("r#{}", lowercased_name)
74 }
75
76 output.push_str(&format!(
77 " #[serde(rename = \"{}\")]\n",
78 field.name.to_lowercase()
79 ));
80
81 if field.is_required {
82 output.push_str(&format!(
83 " pub {}: {},\n",
84 lowercased_name,
85 field_type
86 ));
87 } else {
88 output.push_str(&format!(
89 " pub {}: Option<{}>,\n",
90 lowercased_name,
91 field_type
92 ));
93 }
94 }
95
96 output.push_str("}\n\n");
97 Ok(output)
98}
99
100fn generate_request_model(request: &RequestModel) -> Result<String> {
101 let mut output = String::new();
102 tracing::info!("Generating request model");
103 tracing::info!("{:#?}", request);
104
105 if request.name.is_empty() || request.name == EMPTY_REQUEST_NAME {
106 return Ok(String::new());
107 }
108
109 output.push_str(&format!("/// {}\n", request.name));
110 output.push_str(&"#[derive(Debug, Serialize)]\n".to_string());
111 output.push_str(&format!("pub struct {} {{\n", request.name));
112 output.push_str(&" pub content_type: String,\n".to_string());
113 output.push_str(&format!(" pub body: {},\n", request.schema));
114 output.push_str("}\n");
115 Ok(output)
116}
117
118fn generate_response_model(response: &ResponseModel) -> Result<String> {
119 let mut output = String::new();
120
121 if response.name.is_empty() || response.name == EMPTY_RESPONSE_NAME {
123 return Ok(String::new());
124 }
125
126 output.push_str(&format!("/// {}\n", response.name));
127 output.push_str(&"#[derive(Debug, Deserialize)]\n".to_string());
128 output.push_str(&format!("pub struct {} {{\n", response.name));
129 output.push_str(&" pub status_code: String,\n".to_string());
130 output.push_str(&" pub content_type: String,\n".to_string());
131 output.push_str(&format!(" pub body: {},\n", response.schema));
132 if let Some(desc) = &response.description {
133 output.push_str(&format!(" /// {}\n", desc));
134 }
135 output.push_str("}\n");
136 Ok(output)
137}
138
139
140pub fn generate_rust_code(models: &[Model]) -> Result<String> {
141 let mut code = String::new();
142
143 code.push_str("use serde::{Serialize, Deserialize};\n");
144 code.push_str("use uuid::Uuid;\n");
145 code.push_str("use chrono::{DateTime, NaiveDate, Utc};\n\n");
146
147 for model in models {
148 code.push_str(&format!("/// {}\n", model.name));
149 code.push_str(&"#[derive(Debug, Serialize, Deserialize)]\n".to_string());
150 code.push_str(&format!("pub struct {} {{\n", model.name));
151
152 for field in &model.fields {
153 let field_type = match field.field_type.as_str() {
154 "String" => "String",
155 "f64" => "f64",
156 "i64" => "i64",
157 "bool" => "bool",
158 "DateTime" => "DateTime<Utc>",
159 "Date" => "NaiveDate",
160 "Uuid" => "Uuid",
161 _ => &field.field_type,
162 };
163
164 let mut lowercased_name = field.name.to_lowercase();
165 if is_reserved_word(&lowercased_name) {
166 lowercased_name = format!("r#{}", lowercased_name)
167 }
168
169 code.push_str(&format!(
170 " #[serde(rename = \"{}\")]\n",
171 field.name.to_lowercase()
172 ));
173
174 if field.is_required {
175 code.push_str(&format!(
176 " pub {}: {},\n",
177 lowercased_name,
178 field_type
179 ));
180 } else {
181 code.push_str(&format!(
182 " pub {}: Option<{}>,\n",
183 lowercased_name,
184 field_type
185 ));
186 }
187 }
188
189 code.push_str("}\n\n");
190 }
191
192 Ok(code)
193}
194
195pub fn generate_lib() -> Result<String> {
196 let mut code = String::new();
197 code.push_str("pub mod models;\n");
198
199 Ok(code)
200}