mockforge_core/openapi_routes/
generation.rs1use crate::openapi::route::OpenApiRoute;
7use crate::openapi::spec::OpenApiSpec;
8use std::sync::Arc;
9
10pub fn generate_routes_from_spec(spec: &Arc<OpenApiSpec>) -> Vec<OpenApiRoute> {
12 let mut routes = Vec::new();
13
14 for (path, path_item) in &spec.spec.paths.paths {
15 if let Some(item) = path_item.as_item() {
16 if let Some(op) = &item.get {
18 routes.push(OpenApiRoute::from_operation("GET", path.clone(), op, spec.clone()));
19 }
20 if let Some(op) = &item.post {
21 routes.push(OpenApiRoute::from_operation("POST", path.clone(), op, spec.clone()));
22 }
23 if let Some(op) = &item.put {
24 routes.push(OpenApiRoute::from_operation("PUT", path.clone(), op, spec.clone()));
25 }
26 if let Some(op) = &item.delete {
27 routes.push(OpenApiRoute::from_operation("DELETE", path.clone(), op, spec.clone()));
28 }
29 if let Some(op) = &item.patch {
30 routes.push(OpenApiRoute::from_operation("PATCH", path.clone(), op, spec.clone()));
31 }
32 if let Some(op) = &item.head {
33 routes.push(OpenApiRoute::from_operation("HEAD", path.clone(), op, spec.clone()));
34 }
35 if let Some(op) = &item.options {
36 routes.push(OpenApiRoute::from_operation(
37 "OPTIONS",
38 path.clone(),
39 op,
40 spec.clone(),
41 ));
42 }
43 if let Some(op) = &item.trace {
44 routes.push(OpenApiRoute::from_operation("TRACE", path.clone(), op, spec.clone()));
45 }
46 }
47 }
48
49 routes
50}
51
52pub fn extract_path_parameters(path_template: &str) -> Vec<String> {
54 let mut params = Vec::new();
55 let mut in_param = false;
56 let mut current_param = String::new();
57
58 for ch in path_template.chars() {
59 match ch {
60 '{' => {
61 in_param = true;
62 current_param.clear();
63 }
64 '}' => {
65 if in_param {
66 params.push(current_param.clone());
67 in_param = false;
68 }
69 }
70 ch if in_param => {
71 current_param.push(ch);
72 }
73 _ => {}
74 }
75 }
76
77 params
78}
79
80pub fn convert_path_to_axum_format(path: &str) -> String {
82 path.to_string()
84}
85
86pub fn validate_path_parameters(template_path: &str, actual_path: &str) -> bool {
88 let routing_template = convert_path_to_axum_format(template_path);
90
91 route_matches_pattern(&routing_template, actual_path)
94}
95
96pub fn generate_route_key(method: &str, path: &str) -> String {
98 format!("{}:{}", method.to_uppercase(), path)
99}
100
101pub fn route_matches_pattern(route_path: &str, request_path: &str) -> bool {
103 let route_parts: Vec<&str> = route_path.split('/').filter(|s| !s.is_empty()).collect();
104 let request_parts: Vec<&str> = request_path.split('/').filter(|s| !s.is_empty()).collect();
105
106 match_segments(&route_parts, &request_parts, 0, 0)
107}
108
109fn match_segments(
111 route_parts: &[&str],
112 request_parts: &[&str],
113 route_idx: usize,
114 request_idx: usize,
115) -> bool {
116 if route_idx == route_parts.len() && request_idx == request_parts.len() {
118 return true;
119 }
120
121 if route_idx == route_parts.len() {
123 return false;
124 }
125
126 let current_route = route_parts[route_idx];
127
128 match current_route {
129 "*" => {
130 if request_idx < request_parts.len() {
132 if match_segments(route_parts, request_parts, route_idx + 1, request_idx + 1) {
134 return true;
135 }
136 }
137 false
138 }
139 "**" => {
140 if match_segments(route_parts, request_parts, route_idx + 1, request_idx) {
143 return true;
144 }
145 if request_idx < request_parts.len()
147 && match_segments(route_parts, request_parts, route_idx, request_idx + 1)
148 {
149 return true;
150 }
151 false
152 }
153 route_seg if route_seg.starts_with(':') => {
154 if request_idx < request_parts.len() {
156 return match_segments(route_parts, request_parts, route_idx + 1, request_idx + 1);
157 }
158 false
159 }
160 _ => {
161 if request_idx < request_parts.len() && current_route == request_parts[request_idx] {
163 return match_segments(route_parts, request_parts, route_idx + 1, request_idx + 1);
164 }
165 false
166 }
167 }
168}
169
170pub fn generate_parameter_extraction_code(route: &OpenApiRoute) -> String {
172 let mut code = String::new();
173
174 for param_name in &route.parameters {
176 if param_name.starts_with(':') {
177 code.push_str(&format!(
178 "let {} = path_params.get(\"{}\").cloned().unwrap_or_default();\n",
179 param_name.trim_start_matches(':'),
180 param_name.trim_start_matches(':')
181 ));
182 }
183 }
184
185 code
186}
187
188pub fn generate_parameter_validation_code(route: &OpenApiRoute) -> String {
190 let mut code = String::new();
191
192 for param in &route.parameters {
194 if param.starts_with(':') {
195 code.push_str(&format!(
196 "if {}.is_empty() {{ return Err(Error::generic(\"Missing parameter: {}\")); }}\n",
197 param.trim_start_matches(':'),
198 param.trim_start_matches(':')
199 ));
200 }
201 }
202
203 code
204}
205
206pub fn generate_mock_response_code(route: &OpenApiRoute) -> String {
208 let mut code = String::new();
209
210 code.push_str("let mut response = json!({});\n");
211
212 let _operation = &route.operation;
214 code.push_str("// Generate response based on OpenAPI operation\n");
215 code.push_str(&format!("// Operation: {} {}\n", route.method, route.path));
216
217 code
218}