turbomcp_openapi/
parser.rs1use std::path::Path;
4
5use openapiv3::OpenAPI;
6
7use crate::error::{OpenApiError, Result};
8
9pub fn parse_spec(content: &str) -> Result<OpenAPI> {
13 if content.trim_start().starts_with('{') {
15 return serde_json::from_str(content).map_err(Into::into);
16 }
17
18 serde_yaml::from_str(content).map_err(Into::into)
20}
21
22pub fn load_from_file(path: &Path) -> Result<OpenAPI> {
24 let content = std::fs::read_to_string(path)?;
25 parse_spec(&content)
26}
27
28pub async fn fetch_from_url(url: &str) -> Result<OpenAPI> {
30 let response = reqwest::get(url).await?;
31
32 if !response.status().is_success() {
33 return Err(OpenApiError::ApiError(format!(
34 "HTTP {} fetching OpenAPI spec",
35 response.status()
36 )));
37 }
38
39 let content = response.text().await?;
40 parse_spec(&content)
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 const SIMPLE_SPEC_JSON: &str = r#"{
48 "openapi": "3.0.0",
49 "info": {
50 "title": "Test API",
51 "version": "1.0.0"
52 },
53 "paths": {
54 "/users": {
55 "get": {
56 "summary": "List users",
57 "responses": {
58 "200": {
59 "description": "Success"
60 }
61 }
62 }
63 }
64 }
65 }"#;
66
67 const SIMPLE_SPEC_YAML: &str = r#"
68openapi: "3.0.0"
69info:
70 title: Test API
71 version: "1.0.0"
72paths:
73 /users:
74 get:
75 summary: List users
76 responses:
77 "200":
78 description: Success
79"#;
80
81 #[test]
82 fn test_parse_json() {
83 let spec = parse_spec(SIMPLE_SPEC_JSON).unwrap();
84 assert_eq!(spec.info.title, "Test API");
85 assert!(spec.paths.paths.contains_key("/users"));
86 }
87
88 #[test]
89 fn test_parse_yaml() {
90 let spec = parse_spec(SIMPLE_SPEC_YAML).unwrap();
91 assert_eq!(spec.info.title, "Test API");
92 assert!(spec.paths.paths.contains_key("/users"));
93 }
94
95 #[test]
96 fn test_invalid_spec() {
97 let result = parse_spec("not valid openapi");
98 assert!(result.is_err());
99 }
100}