hemmer_provider_generator_parser/openapi/
parser.rs

1//! OpenAPI spec file parser
2
3use super::types::OpenApiSpec;
4use hemmer_provider_generator_common::{GeneratorError, Result, ServiceDefinition};
5use std::fs;
6use std::path::Path;
7
8/// OpenAPI specification parser
9///
10/// Reads and parses OpenAPI 3.0 specifications from Kubernetes, Azure, or any
11/// OpenAPI 3.0 compliant API.
12pub struct OpenApiParser {
13    /// Loaded OpenAPI spec
14    spec: OpenApiSpec,
15
16    /// Service name (e.g., "kubernetes", "compute")
17    service_name: String,
18
19    /// API version
20    api_version: String,
21
22    /// Provider type hint (optional)
23    provider_hint: Option<ProviderHint>,
24}
25
26/// Provider type hint for OpenAPI specs
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum ProviderHint {
29    /// Kubernetes API
30    Kubernetes,
31
32    /// Azure Resource Manager
33    Azure,
34
35    /// Generic OpenAPI spec
36    Generic,
37}
38
39impl OpenApiParser {
40    /// Load OpenAPI spec from file path
41    ///
42    /// # Example
43    /// ```rust,ignore
44    /// let parser = OpenApiParser::from_file(
45    ///     "k8s-openapi.json",
46    ///     "kubernetes",
47    ///     "1.27.0"
48    /// )?;
49    /// ```
50    pub fn from_file<P: AsRef<Path>>(
51        path: P,
52        service_name: &str,
53        api_version: &str,
54    ) -> Result<Self> {
55        let content = fs::read_to_string(path.as_ref()).map_err(|e| {
56            GeneratorError::Parse(format!(
57                "Failed to read OpenAPI file {}: {}",
58                path.as_ref().display(),
59                e
60            ))
61        })?;
62
63        Self::from_json(&content, service_name, api_version)
64    }
65
66    /// Parse OpenAPI spec from JSON string
67    pub fn from_json(json: &str, service_name: &str, api_version: &str) -> Result<Self> {
68        let spec: OpenApiSpec = serde_json::from_str(json)
69            .map_err(|e| GeneratorError::Parse(format!("Failed to parse OpenAPI JSON: {}", e)))?;
70
71        Ok(Self {
72            spec,
73            service_name: service_name.to_string(),
74            api_version: api_version.to_string(),
75            provider_hint: None,
76        })
77    }
78
79    /// Set provider hint for better parsing
80    pub fn with_provider_hint(mut self, hint: ProviderHint) -> Self {
81        self.provider_hint = Some(hint);
82        self
83    }
84
85    /// Parse OpenAPI spec into ServiceDefinition IR
86    pub fn parse(&self) -> Result<ServiceDefinition> {
87        // Use the converter module to transform OpenAPI -> ServiceDefinition
88        super::converter::convert_openapi_to_service_definition(
89            &self.spec,
90            &self.service_name,
91            &self.api_version,
92            self.provider_hint,
93        )
94    }
95
96    /// Get reference to the underlying OpenAPI spec
97    pub fn spec(&self) -> &OpenApiSpec {
98        &self.spec
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_parse_minimal_openapi() {
108        let openapi_json = r#"{
109            "openapi": "3.0.0",
110            "info": {
111                "title": "Test API",
112                "version": "1.0.0"
113            },
114            "paths": {}
115        }"#;
116
117        let parser = OpenApiParser::from_json(openapi_json, "test", "1.0.0");
118        assert!(parser.is_ok());
119
120        let parser = parser.unwrap();
121        assert_eq!(parser.spec.openapi, "3.0.0");
122        assert_eq!(parser.spec.info.title, "Test API");
123    }
124}