use crate::ast::Crate;
use anyhow::Result;
use std::fmt::Write;
pub struct RustCodegen {
output: String,
indent: usize,
include_header: bool,
}
impl RustCodegen {
pub fn new() -> Self {
Self { output: String::new(), indent: 0, include_header: false }
}
pub fn with_header() -> Self {
Self { output: String::new(), indent: 0, include_header: true }
}
pub(crate) fn indent(&mut self) {
self.indent += 1;
}
pub(crate) fn dedent(&mut self) {
assert!(self.indent > 0, "Cannot dedent below 0");
self.indent -= 1;
}
pub(crate) fn write_indent(&mut self) {
for _ in 0..self.indent {
self.output.push_str(" ");
}
}
pub(crate) fn write(&mut self, s: &str) {
self.output.push_str(s);
}
pub(crate) fn writeln(&mut self) {
self.output.push('\n');
}
#[allow(dead_code)] fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> Result<()> {
write!(&mut self.output, "{}", args)?;
Ok(())
}
#[cfg(test)]
pub(crate) fn output(&self) -> &str {
&self.output
}
pub fn generate_crate(&mut self, crate_node: &Crate) -> Result<String> {
if self.include_header {
self.write("// Generated by Oxur AST codegen\n");
self.writeln();
}
for (i, item) in crate_node.items.iter().enumerate() {
if i > 0 {
self.writeln();
}
self.generate_item(item)?;
}
Ok(self.output.clone())
}
}
impl Default for RustCodegen {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_codegen() {
let codegen = RustCodegen::new();
assert_eq!(codegen.output, "");
assert_eq!(codegen.indent, 0);
}
#[test]
fn test_indent_dedent() {
let mut codegen = RustCodegen::new();
assert_eq!(codegen.indent, 0);
codegen.indent();
assert_eq!(codegen.indent, 1);
codegen.indent();
assert_eq!(codegen.indent, 2);
codegen.dedent();
assert_eq!(codegen.indent, 1);
codegen.dedent();
assert_eq!(codegen.indent, 0);
}
#[test]
#[should_panic(expected = "Cannot dedent below 0")]
fn test_dedent_panic() {
let mut codegen = RustCodegen::new();
codegen.dedent(); }
#[test]
fn test_write_indent() {
let mut codegen = RustCodegen::new();
codegen.write_indent();
assert_eq!(codegen.output, "");
codegen.indent();
codegen.write_indent();
assert_eq!(codegen.output, " ");
codegen.output.clear();
codegen.indent();
codegen.write_indent();
assert_eq!(codegen.output, " "); }
#[test]
fn test_write() {
let mut codegen = RustCodegen::new();
codegen.write("hello");
codegen.write(" ");
codegen.write("world");
assert_eq!(codegen.output, "hello world");
}
#[test]
fn test_writeln() {
let mut codegen = RustCodegen::new();
codegen.write("line1");
codegen.writeln();
codegen.write("line2");
assert_eq!(codegen.output, "line1\nline2");
}
#[test]
fn test_write_fmt() {
let mut codegen = RustCodegen::new();
codegen.write_fmt(format_args!("Value: {}", 42)).unwrap();
assert_eq!(codegen.output, "Value: 42");
}
}