openapi_nexus/generators/
orchestrator.rs1use 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
22pub struct OpenApiCodeGenerator {
24 generator_registry: GeneratorRegistry,
25}
26
27impl OpenApiCodeGenerator {
28 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 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 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 let generators = config
164 .global
165 .generators
166 .as_ref()
167 .cloned()
168 .unwrap_or_default();
169
170 for generator_type in generators {
172 tracing::info!("Generating code with generator {}", generator_type);
173
174 if !self.generator_registry.has_generator(generator_type) {
176 error!("Generator {} not found. Skipping.", generator_type);
177 continue;
178 }
179
180 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 let output_dir = config.global.output_for_generator(generator_type);
205
206 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}