TypeScript_Rust_Compiler/
compiler.rs

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