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)
49            .map_err(|e| CompilerError::Io(e))?;
50
51        // Create lexer and tokenize
52        let mut lexer = Lexer::new(input_content);
53        let tokens = lexer.tokenize()?;
54
55        // Create parser and parse AST
56        let mut parser = Parser::new(tokens);
57        let program = parser.parse()?;
58
59        // Create code generator
60        let mut generator = CodeGenerator::new(self.runtime);
61        let rust_code = generator.generate(&program)?;
62
63        // Write output
64        self.write_output(output, &rust_code)?;
65
66        Ok(())
67    }
68
69    /// Write output to file or directory
70    fn write_output(&self, output: &Path, rust_code: &str) -> Result<()> {
71        if output.is_dir() {
72            // Generate multiple files
73            self.write_multiple_files(output, rust_code)?;
74        } else {
75            // Write single file
76            fs::write(output, rust_code)
77                .map_err(|e| CompilerError::Io(e))?;
78        }
79
80        Ok(())
81    }
82
83    /// Write multiple files for a project
84    fn write_multiple_files(&self, output_dir: &Path, rust_code: &str) -> Result<()> {
85        // Create output directory if it doesn't exist
86        fs::create_dir_all(output_dir)
87            .map_err(|e| CompilerError::Io(e))?;
88
89        // Write main.rs
90        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        // Write Cargo.toml
97        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        // Write lib.rs if needed
103        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    /// Generate Cargo.toml for the output project
112    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    /// Generate lib.rs for the output project
141    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    /// Compile multiple files
163    pub fn compile_project(&mut self, input_dir: &Path, output_dir: &Path) -> Result<()> {
164        // Find all TypeScript files
165        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        // Create output directory
174        fs::create_dir_all(output_dir)
175            .map_err(|e| CompilerError::Io(e))?;
176
177        // Compile each file
178        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            // Create directory for output file
186            if let Some(parent) = rust_file.parent() {
187                fs::create_dir_all(parent)
188                    .map_err(|e| CompilerError::Io(e))?;
189            }
190
191            // Compile single file
192            self.compile(&ts_file, &rust_file)?;
193        }
194
195        // Generate project files
196        self.generate_project_files(output_dir)?;
197
198        Ok(())
199    }
200
201    /// Find all TypeScript files in directory
202    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                // Recursively search subdirectories
212                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    /// Generate project files
223    fn generate_project_files(&self, output_dir: &Path) -> Result<()> {
224        // Generate Cargo.toml
225        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        // Generate README.md
231        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        // Generate .gitignore
237        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    /// Generate README.md
246    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    /// Generate .gitignore
279    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    /// Get compiler version
298    pub fn version() -> &'static str {
299        env!("CARGO_PKG_VERSION")
300    }
301
302    /// Get compiler information
303    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}