1use super::*;
6use anyhow::Result;
7use async_trait::async_trait;
8use serde_json;
9
10pub struct OpenApiAdapter;
11
12#[async_trait]
13impl InputAdapter for OpenApiAdapter {
14 fn name(&self) -> &'static str {
15 "OpenAPI"
16 }
17
18 fn supported_formats(&self) -> Vec<&'static str> {
19 vec!["openapi", "swagger", "oas", "api"]
20 }
21
22 async fn can_handle(&self, input: &InputSource) -> bool {
23 match input {
24 InputSource::Path(path) => {
25 if let Some(ext) = path.extension() {
26 let ext = ext.to_string_lossy().to_lowercase();
27 return ext == "yaml" || ext == "yml" || ext == "json";
28 }
29 false
30 }
31 InputSource::Url(url) => {
32 url.contains("swagger") || url.contains("openapi") || url.ends_with("/api-docs")
33 }
34 InputSource::Raw { format_hint, .. } => format_hint
35 .as_ref()
36 .map(|h| h == "openapi" || h == "swagger")
37 .unwrap_or(false),
38 _ => false,
39 }
40 }
41
42 async fn parse(&self, input: InputSource) -> Result<ContextNode> {
43 let spec = match input {
44 InputSource::Path(path) => {
45 let content = std::fs::read_to_string(&path)?;
46 if path
47 .extension()
48 .map(|e| e == "yaml" || e == "yml")
49 .unwrap_or(false)
50 {
51 serde_yaml::from_str(&content)?
52 } else {
53 serde_json::from_str(&content)?
54 }
55 }
56 InputSource::Url(url) => {
57 let response = reqwest::get(url).await?;
58 response.json::<serde_json::Value>().await?
59 }
60 InputSource::Raw { data, .. } => serde_json::from_slice(&data)?,
61 _ => anyhow::bail!("Invalid input for OpenAPI adapter"),
62 };
63
64 self.parse_openapi_spec(&spec)
65 }
66}
67
68impl OpenApiAdapter {
69 fn parse_openapi_spec(&self, spec: &serde_json::Value) -> Result<ContextNode> {
70 let title = spec
71 .get("info")
72 .and_then(|i| i.get("title"))
73 .and_then(|t| t.as_str())
74 .unwrap_or("API");
75
76 let version = spec
77 .get("info")
78 .and_then(|i| i.get("version"))
79 .and_then(|v| v.as_str())
80 .unwrap_or("1.0.0");
81
82 let mut root = ContextNode {
83 id: "api_root".to_string(),
84 name: format!("{} v{}", title, version),
85 node_type: NodeType::ApiSchema,
86 quantum_state: None,
87 children: vec![],
88 metadata: spec.get("info").cloned().unwrap_or_default(),
89 entanglements: vec![],
90 };
91
92 if let Some(paths) = spec.get("paths").and_then(|p| p.as_object()) {
94 let mut path_nodes = Vec::new();
95
96 for (path, methods) in paths {
97 let mut path_node = ContextNode {
98 id: path.clone(),
99 name: path.clone(),
100 node_type: NodeType::ApiEndpoint,
101 quantum_state: None,
102 children: vec![],
103 metadata: serde_json::json!({}),
104 entanglements: vec![],
105 };
106
107 if let Some(methods_obj) = methods.as_object() {
109 for (method, details) in methods_obj {
110 if ["get", "post", "put", "delete", "patch", "head", "options"]
111 .contains(&method.as_str())
112 {
113 let operation_id = details
114 .get("operationId")
115 .and_then(|o| o.as_str())
116 .unwrap_or(method);
117
118 let mut method_node = ContextNode {
119 id: format!("{}_{}", path, method),
120 name: format!("{} {}", method.to_uppercase(), operation_id),
121 node_type: NodeType::ApiEndpoint,
122 quantum_state: Some(
123 self.calculate_endpoint_quantum_state(method, details),
124 ),
125 children: vec![],
126 metadata: details.clone(),
127 entanglements: self
128 .find_endpoint_entanglements(path, method, details),
129 };
130
131 if let Some(params) =
133 details.get("parameters").and_then(|p| p.as_array())
134 {
135 for param in params {
136 let param_name = param
137 .get("name")
138 .and_then(|n| n.as_str())
139 .unwrap_or("param");
140
141 method_node.children.push(ContextNode {
142 id: format!("{}_{}_{}", path, method, param_name),
143 name: format!("param: {}", param_name),
144 node_type: NodeType::ApiSchema,
145 quantum_state: None,
146 children: vec![],
147 metadata: param.clone(),
148 entanglements: vec![],
149 });
150 }
151 }
152
153 path_node.children.push(method_node);
154 }
155 }
156 }
157
158 path_nodes.push(path_node);
159 }
160
161 root.children.push(ContextNode {
163 id: "endpoints".to_string(),
164 name: "Endpoints".to_string(),
165 node_type: NodeType::Directory,
166 quantum_state: None,
167 children: self.organize_by_path_segments(path_nodes),
168 metadata: serde_json::json!({}),
169 entanglements: vec![],
170 });
171 }
172
173 if let Some(components) = spec.get("components").or_else(|| spec.get("definitions")) {
175 root.children.push(self.parse_schemas(components)?);
176 }
177
178 Ok(root)
179 }
180
181 fn calculate_endpoint_quantum_state(
182 &self,
183 method: &str,
184 details: &serde_json::Value,
185 ) -> QuantumState {
186 let complexity = details
188 .get("parameters")
189 .and_then(|p| p.as_array())
190 .map(|a| a.len())
191 .unwrap_or(0) as f64;
192
193 let has_security = details.get("security").is_some();
194
195 QuantumState {
196 amplitude: match method {
197 "get" => 0.9, "post" => 0.7, "put" => 0.6, "delete" => 0.5, _ => 0.8,
202 },
203 frequency: 1.0 + complexity, phase: if has_security {
205 std::f64::consts::PI / 2.0
206 } else {
207 0.0
208 },
209 collapse_probability: if method == "delete" { 0.9 } else { 0.3 },
210 }
211 }
212
213 fn find_endpoint_entanglements(
214 &self,
215 path: &str,
216 _method: &str,
217 details: &serde_json::Value,
218 ) -> Vec<Entanglement> {
219 let mut entanglements = Vec::new();
220
221 if let Some(schema_ref) = details
223 .get("requestBody")
224 .and_then(|r| r.get("content"))
225 .and_then(|c| c.get("application/json"))
226 .and_then(|j| j.get("schema"))
227 .and_then(|s| s.get("$ref"))
228 .and_then(|r| r.as_str())
229 {
230 if let Some(schema_name) = schema_ref.split('/').next_back() {
231 entanglements.push(Entanglement {
232 target_id: format!("schema_{}", schema_name),
233 strength: 0.9,
234 relationship: "uses_schema".to_string(),
235 });
236 }
237 }
238
239 let resource = path.split('/').nth(1).unwrap_or("");
241 if !resource.is_empty() {
242 entanglements.push(Entanglement {
243 target_id: format!("resource_{}", resource),
244 strength: 0.7,
245 relationship: "same_resource".to_string(),
246 });
247 }
248
249 entanglements
250 }
251
252 fn organize_by_path_segments(&self, paths: Vec<ContextNode>) -> Vec<ContextNode> {
253 paths
256 }
257
258 fn parse_schemas(&self, components: &serde_json::Value) -> Result<ContextNode> {
259 let mut schemas_node = ContextNode {
260 id: "schemas".to_string(),
261 name: "Schemas".to_string(),
262 node_type: NodeType::Directory,
263 quantum_state: None,
264 children: vec![],
265 metadata: serde_json::json!({}),
266 entanglements: vec![],
267 };
268
269 if let Some(schemas) = components.get("schemas").and_then(|s| s.as_object()) {
270 for (name, schema) in schemas {
271 schemas_node.children.push(ContextNode {
272 id: format!("schema_{}", name),
273 name: name.clone(),
274 node_type: NodeType::ApiSchema,
275 quantum_state: None,
276 children: self.parse_schema_properties(schema),
277 metadata: schema.clone(),
278 entanglements: vec![],
279 });
280 }
281 }
282
283 Ok(schemas_node)
284 }
285
286 fn parse_schema_properties(&self, schema: &serde_json::Value) -> Vec<ContextNode> {
287 let mut props = Vec::new();
288
289 if let Some(properties) = schema.get("properties").and_then(|p| p.as_object()) {
290 for (prop_name, prop_schema) in properties {
291 props.push(ContextNode {
292 id: format!("prop_{}", prop_name),
293 name: prop_name.clone(),
294 node_type: NodeType::ApiSchema,
295 quantum_state: None,
296 children: vec![],
297 metadata: prop_schema.clone(),
298 entanglements: vec![],
299 });
300 }
301 }
302
303 props
304 }
305}