Skip to main content

openapi_nexus/generators/
orchestrator.rs

1//! Main code generation orchestrator
2
3use std::path::Path;
4
5use tracing::{error, info};
6
7use super::registry::GeneratorRegistry;
8use crate::codegen::GeneratorType;
9use crate::codegen::traits::{CodeGenerator, FileWriter};
10use crate::config::Config;
11use crate::generators::go::http::GoHttpCodeGenerator;
12use crate::generators::java::okhttp::JavaOkhttpCodeGenerator;
13use crate::generators::kotlin::okhttp::KotlinOkhttpCodeGenerator;
14use crate::generators::python::httpx::PythonHttpxCodeGenerator;
15use crate::generators::python::requests::PythonRequestsCodeGenerator;
16use crate::generators::rust::aioduct::RustAioductCodeGenerator;
17use crate::generators::rust::reqwest::RustReqwestCodeGenerator;
18use crate::generators::rust::ureq::RustUreqCodeGenerator;
19use crate::generators::typescript::fetch::TypeScriptFetchCodeGenerator;
20use crate::parser::parse_file;
21
22/// Main code generation orchestrator
23pub struct OpenApiCodeGenerator {
24    generator_registry: GeneratorRegistry,
25}
26
27impl OpenApiCodeGenerator {
28    /// Create a new code generator with default configuration
29    pub fn new(config: &Config) -> Self {
30        let mut generator_registry = GeneratorRegistry::new();
31        generator_registry.register_generator(
32            GeneratorType::TypeScriptFetch,
33            TypeScriptFetchCodeGenerator::new(
34                config
35                    .generators
36                    .get(&GeneratorType::TypeScriptFetch)
37                    .cloned()
38                    .unwrap_or_default(),
39            ),
40        );
41        generator_registry.register_generator(
42            GeneratorType::GoHttp,
43            GoHttpCodeGenerator::new(
44                config
45                    .generators
46                    .get(&GeneratorType::GoHttp)
47                    .cloned()
48                    .unwrap_or_default(),
49            ),
50        );
51        generator_registry.register_generator(
52            GeneratorType::RustReqwest,
53            RustReqwestCodeGenerator::new(
54                config
55                    .generators
56                    .get(&GeneratorType::RustReqwest)
57                    .cloned()
58                    .unwrap_or_default(),
59            ),
60        );
61        generator_registry.register_generator(
62            GeneratorType::RustUreq,
63            RustUreqCodeGenerator::new(
64                config
65                    .generators
66                    .get(&GeneratorType::RustUreq)
67                    .cloned()
68                    .unwrap_or_default(),
69            ),
70        );
71        generator_registry.register_generator(
72            GeneratorType::RustAioduct,
73            RustAioductCodeGenerator::new(
74                config
75                    .generators
76                    .get(&GeneratorType::RustAioduct)
77                    .cloned()
78                    .unwrap_or_default(),
79            ),
80        );
81        generator_registry.register_generator(
82            GeneratorType::PythonHttpx,
83            PythonHttpxCodeGenerator::new(
84                config
85                    .generators
86                    .get(&GeneratorType::PythonHttpx)
87                    .cloned()
88                    .unwrap_or_default(),
89            ),
90        );
91        generator_registry.register_generator(
92            GeneratorType::PythonRequests,
93            PythonRequestsCodeGenerator::new(
94                config
95                    .generators
96                    .get(&GeneratorType::PythonRequests)
97                    .cloned()
98                    .unwrap_or_default(),
99            ),
100        );
101        generator_registry.register_generator(
102            GeneratorType::KotlinOkhttp,
103            KotlinOkhttpCodeGenerator::new(
104                config
105                    .generators
106                    .get(&GeneratorType::KotlinOkhttp)
107                    .cloned()
108                    .unwrap_or_default(),
109            ),
110        );
111        generator_registry.register_generator(
112            GeneratorType::JavaOkhttp,
113            JavaOkhttpCodeGenerator::new(
114                config
115                    .generators
116                    .get(&GeneratorType::JavaOkhttp)
117                    .cloned()
118                    .unwrap_or_default(),
119            ),
120        );
121
122        Self { generator_registry }
123    }
124
125    /// Register a generator
126    pub fn register_generator<G>(&mut self, generator_type: GeneratorType, generator: G)
127    where
128        G: CodeGenerator + FileWriter + Send + Sync + 'static,
129    {
130        self.generator_registry
131            .register_generator(generator_type, generator)
132    }
133
134    /// Generate code from the configuration
135    /// Logs errors and continues to generate the next generator instead of returning errors
136    pub fn generate(&self, config: &Config) {
137        info!("Parsing OpenAPI specification from: {}", config.input);
138        let parsed = match parse_file(Path::new(&config.input)) {
139            Ok(parsed) => parsed,
140            Err(e) => {
141                error!(
142                    "Failed to parse OpenAPI file {:?}: {}. Skipping code generation.",
143                    config.input, e
144                );
145                return;
146            }
147        };
148
149        let ir = match crate::ir::lower::lower(parsed) {
150            Ok(ir) => ir,
151            Err(e) => {
152                error!("Failed to lower OpenAPI spec to IR: {}. Skipping.", e);
153                return;
154            }
155        };
156        info!(
157            "Lowered to IR: {} schemas, {} operations",
158            ir.schemas.len(),
159            ir.operations.len()
160        );
161
162        // Get generators from config
163        let generators = config
164            .global
165            .generators
166            .as_ref()
167            .cloned()
168            .unwrap_or_default();
169
170        // Generate code for each generator
171        for generator_type in generators {
172            tracing::info!("Generating code with generator {}", generator_type);
173
174            // Check if generator is registered
175            if !self.generator_registry.has_generator(generator_type) {
176                error!("Generator {} not found. Skipping.", generator_type);
177                continue;
178            }
179
180            // Get the generator and generate files
181            let generator = match self.generator_registry.get_generator(generator_type) {
182                Some(generator) => generator,
183                None => {
184                    error!(
185                        "Generator {} not found in registry. Skipping.",
186                        generator_type
187                    );
188                    continue;
189                }
190            };
191
192            let files = match generator.generate(&ir) {
193                Ok(files) => files,
194                Err(e) => {
195                    error!(
196                        "Failed to generate code with generator {}: {}. Continuing to next generator.",
197                        generator_type, e
198                    );
199                    continue;
200                }
201            };
202
203            // Get output directory for this generator
204            let output_dir = config.global.output_for_generator(generator_type);
205
206            // Write files using the FileWriter trait
207            if let Err(e) = generator.write_files(Path::new(&output_dir), &files) {
208                error!(
209                    "Failed to write files for generator {}: {}. Continuing to next generator.",
210                    generator_type, e
211                );
212                continue;
213            }
214
215            info!(
216                "Successfully generated {} files with generator {}",
217                files.len(),
218                generator_type
219            );
220        }
221    }
222}