TypeScript_Rust_Compiler/
compiler.rs1use crate::error::{CompilerError, Result};
4use crate::generator::CodeGenerator;
5use crate::lexer::Lexer;
6use crate::parser::Parser;
7use std::fs;
8use std::path::{Path, PathBuf};
9
10pub struct Compiler {
12 optimize: bool,
13 runtime: bool,
14 output_dir: Option<PathBuf>,
15}
16
17impl Compiler {
18 pub fn new() -> Self {
20 Self {
21 optimize: false,
22 runtime: false,
23 output_dir: None,
24 }
25 }
26
27 pub fn with_optimization(mut self, optimize: bool) -> Self {
29 self.optimize = optimize;
30 self
31 }
32
33 pub fn with_runtime(mut self, runtime: bool) -> Self {
35 self.runtime = runtime;
36 self
37 }
38
39 pub fn with_output_dir(mut self, output_dir: PathBuf) -> Self {
41 self.output_dir = Some(output_dir);
42 self
43 }
44
45 pub fn compile(&mut self, input: &Path, output: &Path) -> Result<()> {
47 let input_content = fs::read_to_string(input)
49 .map_err(|e| CompilerError::Io(e))?;
50
51 let mut lexer = Lexer::new(input_content);
53 let tokens = lexer.tokenize()?;
54
55 let mut parser = Parser::new(tokens);
57 let program = parser.parse()?;
58
59 let mut generator = CodeGenerator::new(self.runtime);
61 let rust_code = generator.generate(&program)?;
62
63 self.write_output(output, &rust_code)?;
65
66 Ok(())
67 }
68
69 fn write_output(&self, output: &Path, rust_code: &str) -> Result<()> {
71 if output.is_dir() {
72 self.write_multiple_files(output, rust_code)?;
74 } else {
75 fs::write(output, rust_code)
77 .map_err(|e| CompilerError::Io(e))?;
78 }
79
80 Ok(())
81 }
82
83 fn write_multiple_files(&self, output_dir: &Path, rust_code: &str) -> Result<()> {
85 fs::create_dir_all(output_dir)
87 .map_err(|e| CompilerError::Io(e))?;
88
89 let main_rs_path = output_dir.join("src").join("main.rs");
91 fs::create_dir_all(main_rs_path.parent().unwrap())
92 .map_err(|e| CompilerError::Io(e))?;
93 fs::write(&main_rs_path, rust_code)
94 .map_err(|e| CompilerError::Io(e))?;
95
96 let cargo_toml = self.generate_cargo_toml();
98 let cargo_toml_path = output_dir.join("Cargo.toml");
99 fs::write(&cargo_toml_path, cargo_toml)
100 .map_err(|e| CompilerError::Io(e))?;
101
102 let lib_rs_path = output_dir.join("src").join("lib.rs");
104 let lib_rs_content = self.generate_lib_rs();
105 fs::write(&lib_rs_path, lib_rs_content)
106 .map_err(|e| CompilerError::Io(e))?;
107
108 Ok(())
109 }
110
111 fn generate_cargo_toml(&self) -> String {
113 let mut dependencies = vec![
114 "serde = { version = \"1.0\", features = [\"derive\"] }".to_string(),
115 "serde_json = \"1.0\"".to_string(),
116 ];
117
118 if self.runtime {
119 dependencies.push("anyhow = \"1.0\"".to_string());
120 dependencies.push("thiserror = \"1.0\"".to_string());
121 }
122
123 format!(
124 r#"[package]
125name = "generated_rust_project"
126version = "0.1.5"
127edition = "2021"
128
129[dependencies]
130{}
131
132[profile.release]
133opt-level = 3
134lto = true
135"#,
136 dependencies.join("\n")
137 )
138 }
139
140 fn generate_lib_rs(&self) -> String {
142 if self.runtime {
143 r#"
144pub mod runtime;
145pub mod types;
146
147use runtime::*;
148use types::*;
149
150// Re-export commonly used types
151pub use runtime::{Any, Unknown, TypeScriptObject};
152"#
153 .to_string()
154 } else {
155 r#"
156// Generated Rust code
157"#
158 .to_string()
159 }
160 }
161
162 pub fn compile_project(&mut self, input_dir: &Path, output_dir: &Path) -> Result<()> {
164 let ts_files = self.find_typescript_files(input_dir)?;
166
167 if ts_files.is_empty() {
168 return Err(CompilerError::internal_error(
169 "No TypeScript files found in input directory"
170 ));
171 }
172
173 fs::create_dir_all(output_dir)
175 .map_err(|e| CompilerError::Io(e))?;
176
177 for ts_file in ts_files {
179 let relative_path = ts_file.strip_prefix(input_dir)
180 .map_err(|_| CompilerError::internal_error("Failed to strip prefix"))?;
181
182 let rust_file = output_dir.join(relative_path)
183 .with_extension("rs");
184
185 if let Some(parent) = rust_file.parent() {
187 fs::create_dir_all(parent)
188 .map_err(|e| CompilerError::Io(e))?;
189 }
190
191 self.compile(&ts_file, &rust_file)?;
193 }
194
195 self.generate_project_files(output_dir)?;
197
198 Ok(())
199 }
200
201 fn find_typescript_files(&self, dir: &Path) -> Result<Vec<PathBuf>> {
203 let mut ts_files = Vec::new();
204
205 for entry in fs::read_dir(dir)
206 .map_err(|e| CompilerError::Io(e))? {
207 let entry = entry.map_err(|e| CompilerError::Io(e))?;
208 let path = entry.path();
209
210 if path.is_dir() {
211 let sub_files = self.find_typescript_files(&path)?;
213 ts_files.extend(sub_files);
214 } else if path.extension().and_then(|s| s.to_str()) == Some("ts") {
215 ts_files.push(path);
216 }
217 }
218
219 Ok(ts_files)
220 }
221
222 fn generate_project_files(&self, output_dir: &Path) -> Result<()> {
224 let cargo_toml = self.generate_cargo_toml();
226 let cargo_toml_path = output_dir.join("Cargo.toml");
227 fs::write(&cargo_toml_path, cargo_toml)
228 .map_err(|e| CompilerError::Io(e))?;
229
230 let readme = self.generate_readme();
232 let readme_path = output_dir.join("README.md");
233 fs::write(&readme_path, readme)
234 .map_err(|e| CompilerError::Io(e))?;
235
236 let gitignore = self.generate_gitignore();
238 let gitignore_path = output_dir.join(".gitignore");
239 fs::write(&gitignore_path, gitignore)
240 .map_err(|e| CompilerError::Io(e))?;
241
242 Ok(())
243 }
244
245 fn generate_readme(&self) -> String {
247 r#"# Generated Rust Project
248
249This project was generated from TypeScript code using the TypeScript-Rust-Compiler.
250
251## Building
252
253```bash
254cargo build
255```
256
257## Running
258
259```bash
260cargo run
261```
262
263## Testing
264
265```bash
266cargo test
267```
268
269## Features
270
271- Generated from TypeScript source code
272- Full Rust type safety
273- Serde serialization support
274"#
275 .to_string()
276 }
277
278 fn generate_gitignore(&self) -> String {
280 r#"# Rust
281/target/
282Cargo.lock
283
284# IDE
285.vscode/
286.idea/
287*.swp
288*.swo
289
290# OS
291.DS_Store
292Thumbs.db
293"#
294 .to_string()
295 }
296
297 pub fn version() -> &'static str {
299 env!("CARGO_PKG_VERSION")
300 }
301
302 pub fn info(&self) -> String {
304 format!(
305 "TypeScript-Rust-Compiler v{}\nOptimization: {}\nRuntime: {}",
306 Self::version(),
307 self.optimize,
308 self.runtime
309 )
310 }
311}
312
313impl Default for Compiler {
314 fn default() -> Self {
315 Self::new()
316 }
317}