Skip to main content

oag_react/
generator.rs

1use oag_core::ir::IrSpec;
2use oag_core::{CodeGenerator, GeneratedFile};
3use oag_typescript::emitters::scaffold::ScaffoldOptions;
4use oag_typescript::{TypeScriptConfig, TypeScriptGenerator};
5use thiserror::Error;
6
7use crate::emitters;
8
9#[derive(Debug, Error)]
10pub enum ReactError {
11    #[error("typescript generation failed: {0}")]
12    TypeScript(#[from] oag_typescript::generator::TypeScriptError),
13
14    #[error("template render failed: {0}")]
15    Render(String),
16}
17
18/// Configuration for the React generator.
19#[derive(Debug, Clone, Default)]
20pub struct ReactConfig {
21    pub base_url: Option<String>,
22    pub no_jsdoc: bool,
23    /// Generate scaffold files (package.json, tsconfig.json, etc).
24    pub scaffold: Option<ScaffoldOptions>,
25}
26
27/// React/SWR code generator. Produces the TypeScript client files plus React hooks.
28pub struct ReactGenerator;
29
30impl CodeGenerator for ReactGenerator {
31    type Config = ReactConfig;
32    type Error = ReactError;
33
34    fn generate(
35        &self,
36        ir: &IrSpec,
37        config: &Self::Config,
38    ) -> Result<Vec<GeneratedFile>, Self::Error> {
39        // First generate the base TypeScript client files
40        let ts_config = TypeScriptConfig {
41            base_url: config.base_url.clone(),
42            no_jsdoc: config.no_jsdoc,
43            scaffold: config.scaffold.clone(),
44        };
45        let mut files = TypeScriptGenerator.generate(ir, &ts_config)?;
46
47        // Add React-specific files
48        files.push(GeneratedFile {
49            path: "hooks.ts".to_string(),
50            content: emitters::hooks::emit_hooks(ir),
51        });
52
53        files.push(GeneratedFile {
54            path: "provider.ts".to_string(),
55            content: emitters::provider::emit_provider(),
56        });
57
58        // Replace index.ts with React version that includes hooks + provider
59        if let Some(idx) = files.iter().position(|f| f.path == "index.ts") {
60            files[idx] = GeneratedFile {
61                path: "index.ts".to_string(),
62                content: emitters::index::emit_index(),
63            };
64        }
65
66        Ok(files)
67    }
68}