oas_forge/
lib.rs

1#![allow(clippy::collapsible_if)]
2pub mod config;
3pub mod error;
4pub mod generics;
5pub mod index;
6pub mod merger;
7pub mod preprocessor;
8pub mod scanner;
9pub mod visitor;
10
11use config::Config;
12use error::Result;
13use std::path::PathBuf;
14
15/// Main entry point for generating OpenAPI definitions.
16#[derive(Default)]
17pub struct Generator {
18    inputs: Vec<PathBuf>,
19    includes: Vec<PathBuf>,
20    output_path: Option<PathBuf>,
21}
22
23impl Generator {
24    /// Creates a new Generator instance.
25    pub fn new() -> Self {
26        Self::default()
27    }
28
29    /// Configures the generator from a Config object.
30    pub fn with_config(mut self, config: Config) -> Self {
31        if let Some(inputs) = config.input {
32            self.inputs.extend(inputs);
33        }
34        if let Some(includes) = config.include {
35            self.includes.extend(includes);
36        }
37        if let Some(output) = config.output {
38            self.output_path = Some(output);
39        }
40        self
41    }
42
43    /// Adds an input directory to scan.
44    pub fn input<P: Into<PathBuf>>(mut self, path: P) -> Self {
45        self.inputs.push(path.into());
46        self
47    }
48
49    /// Adds a specific file to include.
50    pub fn include<P: Into<PathBuf>>(mut self, path: P) -> Self {
51        self.includes.push(path.into());
52        self
53    }
54
55    /// Sets the output file path.
56    pub fn output<P: Into<PathBuf>>(mut self, path: P) -> Self {
57        self.output_path = Some(path.into());
58        self
59    }
60
61    /// Executes the generation process.
62    pub fn generate(self) -> Result<()> {
63        let output = self.output_path.ok_or_else(|| {
64            std::io::Error::new(std::io::ErrorKind::InvalidInput, "Output path is required")
65        })?;
66
67        // 1. Scan and Extract
68        log::info!(
69            "Scanning directories: {:?} and includes: {:?}",
70            self.inputs,
71            self.includes
72        );
73        let snippets = scanner::scan_directories(&self.inputs, &self.includes)?;
74
75        // 2. Merge
76        log::info!("Merging {} snippets", snippets.len());
77        let merged_value = merger::merge_openapi(snippets)?;
78
79        // 3. Write Output
80        // Ensure parent directory exists
81        if let Some(parent) = output.parent() {
82            std::fs::create_dir_all(parent)?;
83        }
84
85        let file = std::fs::File::create(&output)?;
86        let extension = output
87            .extension()
88            .and_then(|s| s.to_str())
89            .unwrap_or("yaml");
90
91        match extension {
92            "json" => {
93                serde_json::to_writer_pretty(file, &merged_value)?;
94            }
95            "yaml" | "yml" => {
96                serde_yaml::to_writer(file, &merged_value)?;
97            }
98            _ => {
99                serde_yaml::to_writer(file, &merged_value)?;
100            }
101        }
102
103        log::info!("Written output to {:?}", output);
104
105        Ok(())
106    }
107}