1use crate::error::TranspileError;
35use crate::hir::HirModule;
36use anyhow::Result;
37use std::fmt;
38
39#[derive(Debug, thiserror::Error)]
41pub enum ValidationError {
42 #[error("Invalid syntax: {0}")]
43 InvalidSyntax(String),
44
45 #[error("Type error: {0}")]
46 TypeError(String),
47
48 #[error("Unsupported feature: {0}")]
49 UnsupportedFeature(String),
50}
51
52pub trait TranspilationBackend: Send + Sync {
54 #[allow(clippy::result_large_err)]
56 fn transpile(&self, hir: &HirModule) -> Result<String, TranspileError>;
57
58 fn validate_output(&self, code: &str) -> Result<(), ValidationError>;
60
61 fn optimize(&self, hir: &HirModule) -> HirModule {
63 hir.clone()
64 }
65
66 fn target_name(&self) -> &str;
68
69 fn file_extension(&self) -> &str;
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
75pub enum TranspilationTarget {
76 #[default]
78 Rust,
79
80 #[cfg(feature = "ruchy")]
82 Ruchy,
83}
84
85impl fmt::Display for TranspilationTarget {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 Self::Rust => write!(f, "rust"),
89 #[cfg(feature = "ruchy")]
90 Self::Ruchy => write!(f, "ruchy"),
91 }
92 }
93}
94
95impl TranspilationTarget {
96 pub fn file_extension(&self) -> &str {
98 match self {
99 Self::Rust => "rs",
100 #[cfg(feature = "ruchy")]
101 Self::Ruchy => "ruchy",
102 }
103 }
104}
105
106impl std::str::FromStr for TranspilationTarget {
107 type Err = String;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 match s.to_lowercase().as_str() {
111 "rust" | "rs" => Ok(Self::Rust),
112 #[cfg(feature = "ruchy")]
113 "ruchy" | "ruc" => Ok(Self::Ruchy),
114 _ => Err(format!("Unknown transpilation target: {}", s)),
115 }
116 }
117}
118
119pub use crate::error::TranspileError as BackendError;
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use std::str::FromStr;
126
127 #[test]
132 fn test_validation_error_invalid_syntax() {
133 let err = ValidationError::InvalidSyntax("missing semicolon".to_string());
134 assert_eq!(err.to_string(), "Invalid syntax: missing semicolon");
135 }
136
137 #[test]
138 fn test_validation_error_type_error() {
139 let err = ValidationError::TypeError("expected i32, found String".to_string());
140 assert_eq!(err.to_string(), "Type error: expected i32, found String");
141 }
142
143 #[test]
144 fn test_validation_error_unsupported_feature() {
145 let err = ValidationError::UnsupportedFeature("async generators".to_string());
146 assert_eq!(err.to_string(), "Unsupported feature: async generators");
147 }
148
149 #[test]
154 fn test_transpilation_target_default() {
155 let target = TranspilationTarget::default();
156 assert_eq!(target, TranspilationTarget::Rust);
157 }
158
159 #[test]
160 fn test_transpilation_target_display_rust() {
161 let target = TranspilationTarget::Rust;
162 assert_eq!(target.to_string(), "rust");
163 }
164
165 #[test]
166 #[cfg(feature = "ruchy")]
167 fn test_transpilation_target_display_ruchy() {
168 let target = TranspilationTarget::Ruchy;
169 assert_eq!(target.to_string(), "ruchy");
170 }
171
172 #[test]
173 fn test_transpilation_target_file_extension_rust() {
174 let target = TranspilationTarget::Rust;
175 assert_eq!(target.file_extension(), "rs");
176 }
177
178 #[test]
179 #[cfg(feature = "ruchy")]
180 fn test_transpilation_target_file_extension_ruchy() {
181 let target = TranspilationTarget::Ruchy;
182 assert_eq!(target.file_extension(), "ruchy");
183 }
184
185 #[test]
186 fn test_transpilation_target_from_str_rust() {
187 let target = TranspilationTarget::from_str("rust").unwrap();
188 assert_eq!(target, TranspilationTarget::Rust);
189 }
190
191 #[test]
192 fn test_transpilation_target_from_str_rs() {
193 let target = TranspilationTarget::from_str("rs").unwrap();
194 assert_eq!(target, TranspilationTarget::Rust);
195 }
196
197 #[test]
198 fn test_transpilation_target_from_str_rust_uppercase() {
199 let target = TranspilationTarget::from_str("RUST").unwrap();
200 assert_eq!(target, TranspilationTarget::Rust);
201 }
202
203 #[test]
204 #[cfg(feature = "ruchy")]
205 fn test_transpilation_target_from_str_ruchy() {
206 let target = TranspilationTarget::from_str("ruchy").unwrap();
207 assert_eq!(target, TranspilationTarget::Ruchy);
208 }
209
210 #[test]
211 #[cfg(feature = "ruchy")]
212 fn test_transpilation_target_from_str_ruc() {
213 let target = TranspilationTarget::from_str("ruc").unwrap();
214 assert_eq!(target, TranspilationTarget::Ruchy);
215 }
216
217 #[test]
218 fn test_transpilation_target_from_str_invalid() {
219 let result = TranspilationTarget::from_str("python");
220 assert!(result.is_err());
221 assert_eq!(result.unwrap_err(), "Unknown transpilation target: python");
222 }
223
224 #[test]
225 fn test_transpilation_target_from_str_empty() {
226 let result = TranspilationTarget::from_str("");
227 assert!(result.is_err());
228 }
229
230 #[test]
235 fn test_backend_error() {
236 let err = TranspileError::backend_error("backend initialization failed");
237 match err.kind {
239 crate::error::ErrorKind::CodeGenerationError(ref msg) => {
240 assert_eq!(msg, "backend initialization failed");
241 }
242 _ => panic!("Expected CodeGenerationError"),
243 }
244 }
245
246 #[test]
247 fn test_transform_error() {
248 let err = TranspileError::transform_error("AST transformation failed");
249 match err.kind {
251 crate::error::ErrorKind::CodeGenerationError(ref msg) => {
252 assert!(msg.contains("Transformation failed"));
253 assert!(msg.contains("AST transformation failed"));
254 }
255 _ => panic!("Expected CodeGenerationError"),
256 }
257 }
258
259 #[test]
260 fn test_optimization_error() {
261 let err = TranspileError::optimization_error("constant folding failed");
262 match err.kind {
264 crate::error::ErrorKind::InternalError(ref msg) => {
265 assert!(msg.contains("Optimization failed"));
266 assert!(msg.contains("constant folding failed"));
267 }
268 _ => panic!("Expected InternalError"),
269 }
270 }
271
272 #[test]
273 fn test_backend_error_with_string() {
274 let err = TranspileError::backend_error(String::from("dynamic error message"));
275 match err.kind {
277 crate::error::ErrorKind::CodeGenerationError(ref msg) => {
278 assert_eq!(msg, "dynamic error message");
279 }
280 _ => panic!("Expected CodeGenerationError"),
281 }
282 }
283
284 #[test]
289 fn test_transpilation_target_clone() {
290 let target = TranspilationTarget::Rust;
291 let cloned = target;
292 assert_eq!(target, cloned);
293 }
294
295 #[test]
296 fn test_transpilation_target_debug() {
297 let target = TranspilationTarget::Rust;
298 let debug_str = format!("{:?}", target);
299 assert!(debug_str.contains("Rust"));
300 }
301
302 #[test]
303 fn test_transpilation_target_eq() {
304 let target1 = TranspilationTarget::Rust;
305 let target2 = TranspilationTarget::Rust;
306 assert_eq!(target1, target2);
307 }
308
309 #[test]
310 #[cfg(feature = "ruchy")]
311 fn test_transpilation_target_ne() {
312 let target1 = TranspilationTarget::Rust;
313 let target2 = TranspilationTarget::Ruchy;
314 assert_ne!(target1, target2);
315 }
316}