1use crate::{CodeGenerator, Error, Lowerer, Result};
6use oxur_lang::CoreForm;
7use std::path::{Path, PathBuf};
8use std::process::Command;
9
10pub struct Compiler {
12 lowerer: Lowerer,
13 codegen: CodeGenerator,
14 output_dir: PathBuf,
15}
16
17impl Compiler {
18 pub fn new(output_dir: PathBuf) -> Self {
19 Self { lowerer: Lowerer::new(), codegen: CodeGenerator::new(), output_dir }
20 }
21
22 pub fn compile(&mut self, forms: Vec<CoreForm>, output: &Path) -> Result<()> {
24 let ast = self.lowerer.lower(forms)?;
26
27 let source = self.codegen.generate(&ast)?;
29
30 let rs_file = self.output_dir.join("generated.rs");
32 std::fs::write(&rs_file, source)?;
33
34 self.compile_with_rustc(&rs_file, output)?;
36
37 Ok(())
38 }
39
40 fn compile_with_rustc(&self, source: &Path, output: &Path) -> Result<()> {
41 let status = Command::new("rustc").arg(source).arg("-o").arg(output).status()?;
42
43 if !status.success() {
44 return Err(Error::Compile(format!(
45 "rustc failed with exit code: {:?}",
46 status.code()
47 )));
48 }
49
50 Ok(())
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn test_compiler_creation() {
60 let compiler = Compiler::new(PathBuf::from("/tmp"));
61 assert_eq!(compiler.output_dir, PathBuf::from("/tmp"));
62 }
63
64 #[test]
65 fn test_compiler_has_lowerer() {
66 let compiler = Compiler::new(PathBuf::from("/tmp"));
67 assert_eq!(compiler.output_dir, PathBuf::from("/tmp"));
69 }
70
71 #[test]
72 fn test_compiler_has_codegen() {
73 let compiler = Compiler::new(PathBuf::from("/tmp"));
74 assert_eq!(compiler.output_dir, PathBuf::from("/tmp"));
76 }
77
78 #[test]
79 fn test_compiler_output_dir() {
80 let test_dir = PathBuf::from("/var/tmp/test");
81 let compiler = Compiler::new(test_dir.clone());
82 assert_eq!(compiler.output_dir, test_dir);
83 }
84
85 #[test]
86 fn test_compile_with_empty_forms() {
87 use tempfile::TempDir;
88
89 let temp_dir = TempDir::new().unwrap();
90 let output_dir = temp_dir.path().join("build");
91 std::fs::create_dir_all(&output_dir).unwrap();
92
93 let mut compiler = Compiler::new(output_dir.clone());
94 let output_path = temp_dir.path().join("test_output");
95
96 let result = compiler.compile(vec![], &output_path);
99 assert!(result.is_ok() || result.is_err());
102 }
103}