unistructgen_core/
codegen.rs1use crate::IRModule;
8use std::error::Error as StdError;
9
10pub type CodegenResult<T> = std::result::Result<T, Box<dyn StdError + Send + Sync>>;
12
13pub trait CodeGenerator {
51 type Error: StdError + Send + Sync + 'static;
53
54 fn generate(&self, module: &IRModule) -> Result<String, Self::Error>;
69
70 fn language(&self) -> &'static str;
81
82 fn file_extension(&self) -> &str;
93
94 fn validate(&self, _module: &IRModule) -> Result<(), Self::Error> {
108 Ok(())
109 }
110
111 fn format(&self, code: String) -> Result<String, Self::Error> {
124 Ok(code)
125 }
126
127 fn metadata(&self) -> GeneratorMetadata {
132 GeneratorMetadata::default()
133 }
134}
135
136#[derive(Debug, Clone, Default)]
140pub struct GeneratorMetadata {
141 pub version: Option<String>,
143 pub description: Option<String>,
145 pub min_language_version: Option<String>,
147 pub features: Vec<String>,
149 pub custom: std::collections::HashMap<String, String>,
151}
152
153impl GeneratorMetadata {
154 pub fn new() -> Self {
156 Self::default()
157 }
158
159 pub fn with_version(mut self, version: impl Into<String>) -> Self {
161 self.version = Some(version.into());
162 self
163 }
164
165 pub fn with_description(mut self, description: impl Into<String>) -> Self {
167 self.description = Some(description.into());
168 self
169 }
170
171 pub fn with_min_language_version(mut self, version: impl Into<String>) -> Self {
173 self.min_language_version = Some(version.into());
174 self
175 }
176
177 pub fn with_feature(mut self, feature: impl Into<String>) -> Self {
179 self.features.push(feature.into());
180 self
181 }
182
183 pub fn with_custom(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
185 self.custom.insert(key.into(), value.into());
186 self
187 }
188}
189
190pub trait CodeGeneratorExt: CodeGenerator {
192 fn generate_formatted(&self, module: &IRModule) -> Result<String, Self::Error> {
194 let code = self.generate(module)?;
195 self.format(code)
196 }
197
198 fn generate_validated(&self, module: &IRModule) -> Result<String, Self::Error> {
200 self.validate(module)?;
201 self.generate(module)
202 }
203
204 fn generate_complete(&self, module: &IRModule) -> Result<String, Self::Error> {
206 self.validate(module)?;
207 let code = self.generate(module)?;
208 self.format(code)
209 }
210
211 fn generate_or<E>(&self, module: &IRModule) -> Result<String, E>
213 where
214 E: From<Self::Error>,
215 {
216 self.generate(module).map_err(E::from)
217 }
218}
219
220impl<G: CodeGenerator> CodeGeneratorExt for G {}
222
223#[derive(Debug)]
225pub struct MultiGeneratorError {
226 pub generator_name: String,
227 pub message: String,
228}
229
230impl std::fmt::Display for MultiGeneratorError {
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 write!(f, "Generator '{}' failed: {}", self.generator_name, self.message)
233 }
234}
235
236impl StdError for MultiGeneratorError {}
237
238pub struct MultiGenerator {
259 generators: Vec<GeneratorEntry>,
260}
261
262struct GeneratorEntry {
263 name: String,
264 generator: Box<dyn GeneratorWrapper>,
265}
266
267trait GeneratorWrapper {
268 fn generate_wrapped(&self, module: &IRModule) -> Result<String, MultiGeneratorError>;
269 #[allow(dead_code)]
270 fn language(&self) -> &'static str;
271 #[allow(dead_code)]
272 fn file_extension(&self) -> &str;
273}
274
275struct GenWrap<G>(G);
276
277impl<G> GeneratorWrapper for GenWrap<G>
278where
279 G: CodeGenerator,
280{
281 fn generate_wrapped(&self, module: &IRModule) -> Result<String, MultiGeneratorError> {
282 self.0.generate(module).map_err(|e| MultiGeneratorError {
283 generator_name: self.0.language().to_string(),
284 message: e.to_string(),
285 })
286 }
287
288 fn language(&self) -> &'static str {
289 self.0.language()
290 }
291
292 fn file_extension(&self) -> &str {
293 self.0.file_extension()
294 }
295}
296
297impl MultiGenerator {
298 pub fn new() -> Self {
300 Self {
301 generators: Vec::new(),
302 }
303 }
304
305 pub fn add<G>(mut self, name: impl Into<String>, generator: G) -> Self
307 where
308 G: CodeGenerator + 'static,
309 {
310 self.generators.push(GeneratorEntry {
311 name: name.into(),
312 generator: Box::new(GenWrap(generator)),
313 });
314 self
315 }
316
317 pub fn generate_all(
319 &self,
320 module: &IRModule,
321 ) -> Result<Vec<(String, String)>, MultiGeneratorError> {
322 let mut results = Vec::new();
323 for entry in &self.generators {
324 let code = entry.generator.generate_wrapped(module)?;
325 results.push((entry.name.clone(), code));
326 }
327 Ok(results)
328 }
329
330 pub fn generator_names(&self) -> Vec<&str> {
332 self.generators.iter().map(|e| e.name.as_str()).collect()
333 }
334}
335
336impl Default for MultiGenerator {
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345 use crate::{IRModule, IRStruct, IRType};
346
347 struct MockGenerator;
349
350 #[derive(Debug)]
351 struct MockError;
352
353 impl std::fmt::Display for MockError {
354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
355 write!(f, "Mock error")
356 }
357 }
358
359 impl StdError for MockError {}
360
361 impl CodeGenerator for MockGenerator {
362 type Error = MockError;
363
364 fn generate(&self, module: &IRModule) -> Result<String, Self::Error> {
365 Ok(format!("struct {};", module.name))
366 }
367
368 fn language(&self) -> &'static str {
369 "Mock"
370 }
371
372 fn file_extension(&self) -> &str {
373 "mock"
374 }
375 }
376
377 #[test]
378 fn test_generator_trait() {
379 let generator = MockGenerator;
380 let mut module = IRModule::new("Test".to_string());
381 let test_struct = IRStruct::new("Test".to_string());
382 module.add_type(IRType::Struct(test_struct));
383
384 let result = generator.generate(&module);
385 assert!(result.is_ok());
386 assert_eq!(result.unwrap(), "struct Test;");
387 }
388
389 #[test]
390 fn test_generator_metadata() {
391 let generator = MockGenerator;
392 assert_eq!(generator.language(), "Mock");
393 assert_eq!(generator.file_extension(), "mock");
394 }
395
396 #[test]
397 fn test_generator_metadata_builder() {
398 let metadata = GeneratorMetadata::new()
399 .with_version("1.0.0")
400 .with_description("Test generator")
401 .with_min_language_version("2021")
402 .with_feature("generics")
403 .with_custom("author", "test");
404
405 assert_eq!(metadata.version, Some("1.0.0".to_string()));
406 assert_eq!(metadata.description, Some("Test generator".to_string()));
407 assert_eq!(metadata.min_language_version, Some("2021".to_string()));
408 assert_eq!(metadata.features, vec!["generics"]);
409 assert_eq!(metadata.custom.get("author"), Some(&"test".to_string()));
410 }
411
412 #[test]
413 fn test_generator_ext() {
414 let generator = MockGenerator;
415 let mut module = IRModule::new("Test".to_string());
416 let test_struct = IRStruct::new("Test".to_string());
417 module.add_type(IRType::Struct(test_struct));
418
419 let result = generator.generate_validated(&module);
420 assert!(result.is_ok());
421
422 let result = generator.generate_formatted(&module);
423 assert!(result.is_ok());
424
425 let result = generator.generate_complete(&module);
426 assert!(result.is_ok());
427 }
428
429 #[test]
430 fn test_multi_generator() {
431 let multi = MultiGenerator::new()
432 .add("mock1", MockGenerator)
433 .add("mock2", MockGenerator);
434
435 let mut module = IRModule::new("Test".to_string());
436 let test_struct = IRStruct::new("Test".to_string());
437 module.add_type(IRType::Struct(test_struct));
438
439 let results = multi.generate_all(&module).unwrap();
440 assert_eq!(results.len(), 2);
441 assert_eq!(results[0].0, "mock1");
442 assert_eq!(results[1].0, "mock2");
443 }
444}