openapi_from_source/
serializer.rs1use crate::openapi_builder::OpenApiDocument;
7use anyhow::{Context, Result};
8use log::debug;
9use std::fs;
10use std::path::Path;
11
12pub fn serialize_yaml(doc: &OpenApiDocument) -> Result<String> {
45 debug!("Serializing OpenAPI document to YAML");
46 serde_yaml::to_string(doc)
47 .context("Failed to serialize OpenAPI document to YAML")
48}
49
50pub fn serialize_json(doc: &OpenApiDocument) -> Result<String> {
83 debug!("Serializing OpenAPI document to JSON");
84 serde_json::to_string_pretty(doc)
85 .context("Failed to serialize OpenAPI document to JSON")
86}
87
88pub fn write_to_file(content: &str, path: &Path) -> Result<()> {
106 debug!("Writing content to file: {}", path.display());
107
108 if let Some(parent) = path.parent() {
110 fs::create_dir_all(parent)
111 .with_context(|| format!("Failed to create directory: {}", parent.display()))?;
112 }
113
114 fs::write(path, content)
115 .with_context(|| format!("Failed to write to file: {}", path.display()))?;
116
117 debug!("Successfully wrote {} bytes to {}", content.len(), path.display());
118 Ok(())
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::openapi_builder::{Info, OpenApiBuilder, OpenApiDocument};
125 use std::collections::HashMap;
126 use tempfile::TempDir;
127
128 fn create_test_document() -> OpenApiDocument {
130 OpenApiDocument {
131 openapi: "3.0.0".to_string(),
132 info: Info {
133 title: "Test API".to_string(),
134 version: "1.0.0".to_string(),
135 description: Some("A test API".to_string()),
136 },
137 paths: HashMap::new(),
138 components: None,
139 }
140 }
141
142 #[test]
143 fn test_serialize_yaml() {
144 let doc = create_test_document();
145 let result = serialize_yaml(&doc);
146
147 assert!(result.is_ok());
148 let yaml = result.unwrap();
149
150 assert!(yaml.contains("openapi:"));
152 assert!(yaml.contains("3.0.0"));
153 assert!(yaml.contains("info:"));
154 assert!(yaml.contains("title:"));
155 assert!(yaml.contains("Test API"));
156 assert!(yaml.contains("version:"));
157 assert!(yaml.contains("1.0.0"));
158 assert!(yaml.contains("description:"));
159 assert!(yaml.contains("A test API"));
160 assert!(yaml.contains("paths:"));
161 }
162
163 #[test]
164 fn test_serialize_json() {
165 let doc = create_test_document();
166 let result = serialize_json(&doc);
167
168 assert!(result.is_ok());
169 let json = result.unwrap();
170
171 assert!(json.contains("\"openapi\""));
173 assert!(json.contains("\"3.0.0\""));
174 assert!(json.contains("\"info\""));
175 assert!(json.contains("\"title\""));
176 assert!(json.contains("\"Test API\""));
177 assert!(json.contains("\"version\""));
178 assert!(json.contains("\"1.0.0\""));
179 assert!(json.contains("\"description\""));
180 assert!(json.contains("\"A test API\""));
181 assert!(json.contains("\"paths\""));
182
183 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
185 assert_eq!(parsed["openapi"], "3.0.0");
186 assert_eq!(parsed["info"]["title"], "Test API");
187 }
188
189 #[test]
190 fn test_serialize_json_pretty_format() {
191 let doc = create_test_document();
192 let json = serialize_json(&doc).unwrap();
193
194 assert!(json.contains('\n'));
196 assert!(json.contains(" ")); let line_count = json.lines().count();
200 assert!(line_count > 5, "Pretty printed JSON should have multiple lines");
201 }
202
203 #[test]
204 fn test_write_to_file() {
205 let temp_dir = TempDir::new().unwrap();
206 let file_path = temp_dir.path().join("test.yaml");
207 let content = "test content";
208
209 let result = write_to_file(content, &file_path);
210
211 assert!(result.is_ok());
212 assert!(file_path.exists());
213
214 let read_content = fs::read_to_string(&file_path).unwrap();
215 assert_eq!(read_content, content);
216 }
217
218 #[test]
219 fn test_write_to_file_creates_directories() {
220 let temp_dir = TempDir::new().unwrap();
221 let file_path = temp_dir.path().join("subdir").join("nested").join("test.yaml");
222 let content = "test content";
223
224 let result = write_to_file(content, &file_path);
225
226 assert!(result.is_ok());
227 assert!(file_path.exists());
228
229 let read_content = fs::read_to_string(&file_path).unwrap();
230 assert_eq!(read_content, content);
231 }
232
233 #[test]
234 fn test_write_to_file_overwrites_existing() {
235 let temp_dir = TempDir::new().unwrap();
236 let file_path = temp_dir.path().join("test.yaml");
237
238 write_to_file("initial content", &file_path).unwrap();
240
241 let new_content = "new content";
243 let result = write_to_file(new_content, &file_path);
244
245 assert!(result.is_ok());
246
247 let read_content = fs::read_to_string(&file_path).unwrap();
248 assert_eq!(read_content, new_content);
249 }
250
251 #[test]
252 fn test_serialize_yaml_with_complex_document() {
253 use crate::extractor::{HttpMethod, RouteInfo};
254 use crate::parser::AstParser;
255 use crate::schema_generator::SchemaGenerator;
256 use crate::type_resolver::TypeResolver;
257 use std::io::Write;
258
259 let temp_dir = TempDir::new().unwrap();
261 let file_path = temp_dir.path().join("test.rs");
262 let mut file = fs::File::create(&file_path).unwrap();
263 file.write_all(b"pub struct User { pub id: u32, pub name: String }").unwrap();
264
265 let parsed = AstParser::parse_file(&file_path).unwrap();
267 let type_resolver = TypeResolver::new(vec![parsed]);
268 let mut schema_gen = SchemaGenerator::new(type_resolver);
269
270 let mut builder = OpenApiBuilder::new();
271 let route = RouteInfo::new(
272 "/users".to_string(),
273 HttpMethod::Get,
274 "get_users".to_string(),
275 );
276 builder.add_route(&route, &mut schema_gen);
277
278 let doc = builder.build(schema_gen);
279 let yaml = serialize_yaml(&doc).unwrap();
280
281 assert!(yaml.contains("openapi:"));
283 assert!(yaml.contains("paths:"));
284 assert!(yaml.contains("/users:"));
285 assert!(yaml.contains("get:"));
286 }
287
288 #[test]
289 fn test_serialize_json_with_complex_document() {
290 use crate::extractor::{HttpMethod, Parameter, ParameterLocation, RouteInfo, TypeInfo};
291 use crate::parser::AstParser;
292 use crate::schema_generator::SchemaGenerator;
293 use crate::type_resolver::TypeResolver;
294 use std::io::Write;
295
296 let temp_dir = TempDir::new().unwrap();
298 let file_path = temp_dir.path().join("test.rs");
299 let mut file = fs::File::create(&file_path).unwrap();
300 file.write_all(b"pub struct User { pub id: u32, pub name: String }").unwrap();
301
302 let parsed = AstParser::parse_file(&file_path).unwrap();
304 let type_resolver = TypeResolver::new(vec![parsed]);
305 let mut schema_gen = SchemaGenerator::new(type_resolver);
306
307 let mut builder = OpenApiBuilder::new();
308 let mut route = RouteInfo::new(
309 "/users/:id".to_string(),
310 HttpMethod::Get,
311 "get_user".to_string(),
312 );
313 route.parameters.push(Parameter::new(
314 "id".to_string(),
315 ParameterLocation::Path,
316 TypeInfo::new("u32".to_string()),
317 true,
318 ));
319 builder.add_route(&route, &mut schema_gen);
320
321 let doc = builder.build(schema_gen);
322 let json = serialize_json(&doc).unwrap();
323
324 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
326 assert_eq!(parsed["openapi"], "3.0.0");
327 assert!(parsed["paths"].is_object());
328 assert!(parsed["paths"]["/users/{id}"].is_object());
329 assert!(parsed["paths"]["/users/{id}"]["get"].is_object());
330 }
331
332 #[test]
333 fn test_roundtrip_yaml_serialization() {
334 let doc = create_test_document();
335 let yaml = serialize_yaml(&doc).unwrap();
336
337 let deserialized: OpenApiDocument = serde_yaml::from_str(&yaml).unwrap();
339
340 assert_eq!(deserialized.openapi, doc.openapi);
341 assert_eq!(deserialized.info.title, doc.info.title);
342 assert_eq!(deserialized.info.version, doc.info.version);
343 assert_eq!(deserialized.info.description, doc.info.description);
344 }
345
346 #[test]
347 fn test_roundtrip_json_serialization() {
348 let doc = create_test_document();
349 let json = serialize_json(&doc).unwrap();
350
351 let deserialized: OpenApiDocument = serde_json::from_str(&json).unwrap();
353
354 assert_eq!(deserialized.openapi, doc.openapi);
355 assert_eq!(deserialized.info.title, doc.info.title);
356 assert_eq!(deserialized.info.version, doc.info.version);
357 assert_eq!(deserialized.info.description, doc.info.description);
358 }
359
360 #[test]
361 fn test_write_yaml_file_end_to_end() {
362 let temp_dir = TempDir::new().unwrap();
363 let file_path = temp_dir.path().join("openapi.yaml");
364
365 let doc = create_test_document();
366 let yaml = serialize_yaml(&doc).unwrap();
367
368 write_to_file(&yaml, &file_path).unwrap();
369
370 let content = fs::read_to_string(&file_path).unwrap();
372 let deserialized: OpenApiDocument = serde_yaml::from_str(&content).unwrap();
373
374 assert_eq!(deserialized.info.title, "Test API");
375 }
376
377 #[test]
378 fn test_write_json_file_end_to_end() {
379 let temp_dir = TempDir::new().unwrap();
380 let file_path = temp_dir.path().join("openapi.json");
381
382 let doc = create_test_document();
383 let json = serialize_json(&doc).unwrap();
384
385 write_to_file(&json, &file_path).unwrap();
386
387 let content = fs::read_to_string(&file_path).unwrap();
389 let deserialized: OpenApiDocument = serde_json::from_str(&content).unwrap();
390
391 assert_eq!(deserialized.info.title, "Test API");
392 }
393}