fob_gen/
program_builder.rs

1//! Incremental program builder for streaming code generation
2
3use crate::error::Result;
4use crate::format::FormatOptions;
5use oxc_allocator::Allocator;
6use oxc_ast::AstBuilder;
7use oxc_ast::ast::*;
8use oxc_codegen::Codegen;
9use oxc_span::{SPAN, SourceType};
10use std::io::Write;
11
12/// Incremental program builder for streaming code generation
13///
14/// Allows building programs incrementally without requiring all statements
15/// upfront. Useful for generating large manifests or streaming output.
16pub struct ProgramBuilder<'a> {
17    ast: AstBuilder<'a>,
18    body: Vec<Statement<'a>>,
19    source_type: SourceType,
20}
21
22impl<'a> ProgramBuilder<'a> {
23    /// Create a new program builder
24    pub fn new(allocator: &'a Allocator) -> Self {
25        Self {
26            ast: AstBuilder::new(allocator),
27            body: Vec::new(),
28            source_type: SourceType::mjs(),
29        }
30    }
31
32    /// Create a new program builder with specific source type
33    pub fn with_source_type(allocator: &'a Allocator, source_type: SourceType) -> Self {
34        Self {
35            ast: AstBuilder::new(allocator),
36            body: Vec::new(),
37            source_type,
38        }
39    }
40
41    /// Get the underlying AST builder for creating nodes
42    pub fn ast(&self) -> &AstBuilder<'a> {
43        &self.ast
44    }
45
46    /// Get mutable access to the underlying AST builder
47    pub fn ast_mut(&mut self) -> &mut AstBuilder<'a> {
48        &mut self.ast
49    }
50
51    /// Add a statement to the program
52    pub fn push(&mut self, stmt: Statement<'a>) {
53        self.body.push(stmt);
54    }
55
56    /// Add multiple statements to the program
57    pub fn extend(&mut self, stmts: impl IntoIterator<Item = Statement<'a>>) {
58        self.body.extend(stmts);
59    }
60
61    /// Get the current number of statements
62    pub fn len(&self) -> usize {
63        self.body.len()
64    }
65
66    /// Check if the builder is empty
67    pub fn is_empty(&self) -> bool {
68        self.body.is_empty()
69    }
70
71    /// Write the program to a writer with formatting options
72    ///
73    /// Consumes the builder since statements are moved into the program.
74    pub fn write_to<W: Write>(self, writer: &mut W, _opts: &FormatOptions) -> Result<()> {
75        let body_vec = self.ast.vec_from_iter(self.body);
76        let program = self.ast.program(
77            SPAN,
78            self.source_type,
79            "",
80            self.ast.vec(), // imports/exports
81            None,           // hashbang
82            self.ast.vec(), // directives
83            body_vec,
84        );
85
86        let codegen = Codegen::new();
87        let result = codegen.build(&program);
88
89        writer
90            .write_all(result.code.as_bytes())
91            .map_err(|e| crate::error::GenError::CodegenFailed(format!("Write error: {}", e)))?;
92
93        Ok(())
94    }
95
96    /// Generate the complete program as a string
97    ///
98    /// Consumes the builder since statements are moved into the program.
99    pub fn generate(self, _opts: &FormatOptions) -> Result<String> {
100        let body_vec = self.ast.vec_from_iter(self.body);
101        let program = self.ast.program(
102            SPAN,
103            self.source_type,
104            "",
105            self.ast.vec(), // imports/exports
106            None,           // hashbang
107            self.ast.vec(), // directives
108            body_vec,
109        );
110
111        let codegen = Codegen::new();
112        let result = codegen.build(&program);
113
114        Ok(result.code)
115    }
116
117    /// Build the program AST (for advanced usage)
118    ///
119    /// Consumes the builder since statements are moved into the program.
120    pub fn build_program(self) -> Program<'a> {
121        let body_vec = self.ast.vec_from_iter(self.body);
122        self.ast.program(
123            SPAN,
124            self.source_type,
125            "",
126            self.ast.vec(), // imports/exports
127            None,           // hashbang
128            self.ast.vec(), // directives
129            body_vec,
130        )
131    }
132}