openapi_nexus_parser/
parser.rs1use std::fs;
4use std::path::Path;
5
6use tracing::{debug, error};
7use utoipa::openapi::OpenApi;
8
9use crate::error::ParseError;
10use crate::serde_error::SerdeErrorExtractor;
11
12fn extract_error_context(content: &str, error_msg: &str) -> Vec<String> {
13 let (line, column) = SerdeErrorExtractor::new(error_msg).extract_location();
14
15 if line > 0 {
16 debug!("Error at line {}, column {}", line, column);
17
18 let lines: Vec<&str> = content.lines().collect();
19 if line <= lines.len() {
20 let error_line_idx = line - 1;
21 let start_line = error_line_idx.saturating_sub(5);
22 let end_line = (error_line_idx + 10).min(lines.len());
23
24 debug!(
25 "Raw content around error (lines {} to {}):",
26 start_line + 1,
27 end_line
28 );
29 for (i, line_content) in lines.iter().enumerate().take(end_line).skip(start_line) {
30 let line_num = i + 1;
31 let is_error_line = line_num == line;
32 let marker = if is_error_line { ">>>" } else { " " };
33 debug!("{} {} | {}", marker, line_num, line_content);
34 }
35
36 if error_line_idx + 1 < lines.len() {
37 let next_line = lines[error_line_idx + 1];
38 debug!("Next line after error: {}", next_line);
39 debug!(
40 "Next line indentation: {} spaces",
41 next_line.chars().take_while(|c| *c == ' ').count()
42 );
43 }
44
45 let error_line = lines[line - 1];
46 vec![
47 format!("Error at line {}: {}", line, error_line),
48 format!("Column: {}", column),
49 ]
50 } else {
51 vec![format!("Error: {}", error_msg)]
52 }
53 } else {
54 vec![format!("Error: {}", error_msg)]
55 }
56}
57
58pub fn parse_content_json(content: &str) -> Result<OpenApi, ParseError> {
59 let value: serde_json::Value = serde_json::from_str(content).map_err(|e| {
60 let error_msg = e.to_string();
61 debug!("Serde error message: {}", error_msg);
62 let context = extract_error_context(content, &error_msg);
63
64 for line in &context {
65 error!("{}", line);
66 }
67
68 ParseError::JsonParse { source: e, context }
69 })?;
70
71 serde_json::from_value(value).map_err(|e| ParseError::OpenApiDeserializeJson { source: e })
72}
73
74pub fn parse_content_yaml(content: &str) -> Result<OpenApi, ParseError> {
75 let value: serde_norway::Value = serde_norway::from_str(content).map_err(|e| {
76 let error_msg = e.to_string();
77 debug!("Serde error message: {}", error_msg);
78 let context = extract_error_context(content, &error_msg);
79
80 for line in &context {
81 error!("{}", line);
82 }
83
84 ParseError::YamlParse { source: e, context }
85 })?;
86
87 serde_norway::from_value(value).map_err(|e| ParseError::OpenApiDeserializeYaml { source: e })
88}
89
90pub fn parse_file(path: &Path) -> Result<OpenApi, ParseError> {
92 let content = fs::read_to_string(path).map_err(|e| ParseError::FileRead {
93 path: path.to_string_lossy().to_string(),
94 source: e,
95 })?;
96
97 let file_extension = path.extension().and_then(|ext| ext.to_str());
98
99 match file_extension {
100 Some("json") => parse_content_json(&content),
101 Some("yaml") | Some("yml") => parse_content_yaml(&content),
102 Some(ext) => Err(ParseError::UnsupportedFormat {
103 format: ext.to_string(),
104 }),
105 None => Err(ParseError::UnsupportedFormat {
106 format: "<unknown>".to_string(),
107 }),
108 }
109}