openapi_nexus/
openapi_code_generator.rs

1//! Main code generation orchestrator
2
3use std::path::Path;
4
5use tracing::{error, info};
6
7use crate::generator_registry::GeneratorRegistry;
8use openapi_nexus_common::GeneratorType;
9use openapi_nexus_config::Config;
10use openapi_nexus_core::traits::{CodeGenerator, FileWriter};
11use openapi_nexus_go::GoHttpCodeGenerator;
12use openapi_nexus_parser::parse_file;
13use openapi_nexus_typescript::TypeScriptFetchCodeGenerator;
14
15/// Main code generation orchestrator
16pub struct OpenApiCodeGenerator {
17    generator_registry: GeneratorRegistry,
18}
19
20impl OpenApiCodeGenerator {
21    /// Create a new code generator with default configuration
22    pub fn new(config: &Config) -> Self {
23        let mut generator_registry = GeneratorRegistry::new();
24        generator_registry.register_generator(
25            GeneratorType::TypeScriptFetch,
26            TypeScriptFetchCodeGenerator::new(
27                config
28                    .generators
29                    .get(&GeneratorType::TypeScriptFetch)
30                    .cloned()
31                    .unwrap_or_default(),
32            ),
33        );
34        generator_registry.register_generator(
35            GeneratorType::GoHttp,
36            GoHttpCodeGenerator::new(
37                config
38                    .generators
39                    .get(&GeneratorType::GoHttp)
40                    .cloned()
41                    .unwrap_or_default(),
42            ),
43        );
44
45        Self { generator_registry }
46    }
47
48    /// Register a generator
49    pub fn register_generator<G>(&mut self, generator_type: GeneratorType, generator: G)
50    where
51        G: CodeGenerator + FileWriter + Send + Sync + 'static,
52    {
53        self.generator_registry
54            .register_generator(generator_type, generator)
55    }
56
57    /// Generate code from the configuration
58    /// Logs errors and continues to generate the next generator instead of returning errors
59    pub fn generate(&self, config: &Config) {
60        info!("Parsing OpenAPI specification from: {}", config.input);
61        let openapi = match parse_file(Path::new(&config.input)) {
62            Ok(openapi) => openapi,
63            Err(e) => {
64                error!(
65                    "Failed to parse OpenAPI file {:?}: {}. Skipping code generation.",
66                    config.input, e
67                );
68                return;
69            }
70        };
71
72        // Get generators from config
73        let generators = config
74            .global
75            .generators
76            .as_ref()
77            .cloned()
78            .unwrap_or_default();
79
80        // Generate code for each generator
81        for generator_type in generators {
82            tracing::info!("Generating code with generator {}", generator_type);
83
84            // Check if generator is registered
85            if !self.generator_registry.has_generator(generator_type) {
86                error!("Generator {} not found. Skipping.", generator_type);
87                continue;
88            }
89
90            // Get the generator and generate files
91            let generator = match self.generator_registry.get_generator(generator_type) {
92                Some(generator) => generator,
93                None => {
94                    error!(
95                        "Generator {} not found in registry. Skipping.",
96                        generator_type
97                    );
98                    continue;
99                }
100            };
101
102            let files = match generator.generate(&openapi) {
103                Ok(files) => files,
104                Err(e) => {
105                    error!(
106                        "Failed to generate code with generator {}: {}. Continuing to next generator.",
107                        generator_type, e
108                    );
109                    continue;
110                }
111            };
112
113            // Get output directory for this generator
114            let output_dir = config.global.output_for_generator(generator_type);
115
116            // Write files using the FileWriter trait
117            if let Err(e) = generator.write_files(Path::new(&output_dir), &files) {
118                error!(
119                    "Failed to write files for generator {}: {}. Continuing to next generator.",
120                    generator_type, e
121                );
122                continue;
123            }
124
125            info!(
126                "Successfully generated {} files with generator {}",
127                files.len(),
128                generator_type
129            );
130        }
131    }
132}