1use alef_core::ir::{ErrorDef, ErrorVariant};
2
3pub fn gen_pyo3_error_types(error: &ErrorDef, module_name: &str) -> String {
6 let mut lines = Vec::with_capacity(error.variants.len() + 2);
7 lines.push("// Error types".to_string());
8
9 for variant in &error.variants {
11 let variant_name = if variant.name.ends_with("Error") {
12 variant.name.clone()
13 } else {
14 format!("{}Error", variant.name)
15 };
16 lines.push(format!(
17 "pyo3::create_exception!({module_name}, {}, pyo3::exceptions::PyException);",
18 variant_name
19 ));
20 }
21
22 lines.push(format!(
24 "pyo3::create_exception!({module_name}, {}, pyo3::exceptions::PyException);",
25 error.name
26 ));
27
28 lines.join("\n")
29}
30
31pub fn gen_pyo3_error_converter(error: &ErrorDef, core_import: &str) -> String {
34 let rust_path = if error.rust_path.is_empty() {
35 format!("{core_import}::{}", error.name)
36 } else {
37 error.rust_path.replace('-', "_")
38 };
39
40 let fn_name = format!("{}_to_py_err", to_snake_case(&error.name));
41
42 let mut lines = Vec::new();
43 lines.push(format!("/// Convert a `{rust_path}` error to a Python exception."));
44 lines.push(format!("fn {fn_name}(e: {rust_path}) -> pyo3::PyErr {{"));
45 lines.push(" let msg = e.to_string();".to_string());
46 lines.push(" #[allow(unreachable_patterns)]".to_string());
47 lines.push(" match &e {".to_string());
48
49 for variant in &error.variants {
50 let pattern = if variant.is_unit {
51 format!("{rust_path}::{}", variant.name)
52 } else {
53 format!("{rust_path}::{}(..)", variant.name)
54 };
55 let variant_exc_name = if variant.name.ends_with("Error") {
56 variant.name.clone()
57 } else {
58 format!("{}Error", variant.name)
59 };
60 lines.push(format!(" {pattern} => {}::new_err(msg),", variant_exc_name));
61 }
62
63 lines.push(format!(" _ => {}::new_err(msg),", error.name));
65 lines.push(" }".to_string());
66 lines.push("}".to_string());
67 lines.join("\n")
68}
69
70pub fn gen_pyo3_error_registration(error: &ErrorDef) -> Vec<String> {
73 let mut registrations = Vec::with_capacity(error.variants.len() + 1);
74
75 for variant in &error.variants {
76 let variant_exc_name = if variant.name.ends_with("Error") {
77 variant.name.clone()
78 } else {
79 format!("{}Error", variant.name)
80 };
81 registrations.push(format!(
82 " m.add(\"{}\", m.py().get_type::<{}>())?;",
83 variant_exc_name, variant_exc_name
84 ));
85 }
86
87 registrations.push(format!(
89 " m.add(\"{}\", m.py().get_type::<{}>())?;",
90 error.name, error.name
91 ));
92
93 registrations
94}
95
96pub fn converter_fn_name(error: &ErrorDef) -> String {
98 format!("{}_to_py_err", to_snake_case(&error.name))
99}
100
101fn to_snake_case(s: &str) -> String {
103 let mut result = String::with_capacity(s.len() + 4);
104 for (i, c) in s.chars().enumerate() {
105 if c.is_uppercase() {
106 if i > 0 {
107 result.push('_');
108 }
109 result.push(c.to_ascii_lowercase());
110 } else {
111 result.push(c);
112 }
113 }
114 result
115}
116
117pub fn gen_napi_error_types(error: &ErrorDef) -> String {
123 let mut lines = Vec::with_capacity(error.variants.len() + 4);
124 lines.push("// Error variant name constants".to_string());
125 for variant in &error.variants {
126 lines.push(format!(
127 "pub const {}_ERROR_{}: &str = \"{}\";",
128 to_screaming_snake(&error.name),
129 to_screaming_snake(&variant.name),
130 variant.name,
131 ));
132 }
133 lines.join("\n")
134}
135
136pub fn gen_napi_error_converter(error: &ErrorDef, core_import: &str) -> String {
138 let rust_path = if error.rust_path.is_empty() {
139 format!("{core_import}::{}", error.name)
140 } else {
141 error.rust_path.replace('-', "_")
142 };
143
144 let fn_name = format!("{}_to_napi_err", to_snake_case(&error.name));
145
146 let mut lines = Vec::new();
147 lines.push(format!("/// Convert a `{rust_path}` error to a NAPI error."));
148 lines.push("#[allow(dead_code)]".to_string());
149 lines.push(format!("fn {fn_name}(e: {rust_path}) -> napi::Error {{"));
150 lines.push(" let msg = e.to_string();".to_string());
151 lines.push(" #[allow(unreachable_patterns)]".to_string());
152 lines.push(" match &e {".to_string());
153
154 for variant in &error.variants {
155 let pattern = if variant.is_unit {
156 format!("{rust_path}::{}", variant.name)
157 } else {
158 format!("{rust_path}::{}(..)", variant.name)
159 };
160 lines.push(format!(
161 " {pattern} => napi::Error::new(napi::Status::GenericFailure, format!(\"[{}] {{}}\", msg)),",
162 variant.name,
163 ));
164 }
165
166 lines.push(" _ => napi::Error::new(napi::Status::GenericFailure, msg),".to_string());
168 lines.push(" }".to_string());
169 lines.push("}".to_string());
170 lines.join("\n")
171}
172
173pub fn napi_converter_fn_name(error: &ErrorDef) -> String {
175 format!("{}_to_napi_err", to_snake_case(&error.name))
176}
177
178pub fn gen_wasm_error_converter(error: &ErrorDef, core_import: &str) -> String {
184 let rust_path = if error.rust_path.is_empty() {
185 format!("{core_import}::{}", error.name)
186 } else {
187 error.rust_path.replace('-', "_")
188 };
189
190 let fn_name = format!("{}_to_js_value", to_snake_case(&error.name));
191
192 let mut lines = Vec::new();
193 lines.push(format!("/// Convert a `{rust_path}` error to a `JsValue` string."));
194 lines.push("#[allow(dead_code)]".to_string());
195 lines.push(format!("fn {fn_name}(e: {rust_path}) -> wasm_bindgen::JsValue {{"));
196 lines.push(" wasm_bindgen::JsValue::from_str(&e.to_string())".to_string());
197 lines.push("}".to_string());
198 lines.join("\n")
199}
200
201pub fn wasm_converter_fn_name(error: &ErrorDef) -> String {
203 format!("{}_to_js_value", to_snake_case(&error.name))
204}
205
206pub fn gen_php_error_converter(error: &ErrorDef, core_import: &str) -> String {
212 let rust_path = if error.rust_path.is_empty() {
213 format!("{core_import}::{}", error.name)
214 } else {
215 error.rust_path.replace('-', "_")
216 };
217
218 let fn_name = format!("{}_to_php_err", to_snake_case(&error.name));
219
220 let mut lines = Vec::new();
221 lines.push(format!("/// Convert a `{rust_path}` error to a PHP exception."));
222 lines.push("#[allow(dead_code)]".to_string());
223 lines.push(format!(
224 "fn {fn_name}(e: {rust_path}) -> ext_php_rs::exception::PhpException {{"
225 ));
226 lines.push(" let msg = e.to_string();".to_string());
227 lines.push(" #[allow(unreachable_patterns)]".to_string());
228 lines.push(" match &e {".to_string());
229
230 for variant in &error.variants {
231 let pattern = if variant.is_unit {
232 format!("{rust_path}::{}", variant.name)
233 } else {
234 format!("{rust_path}::{}(..)", variant.name)
235 };
236 lines.push(format!(
237 " {pattern} => ext_php_rs::exception::PhpException::default(format!(\"[{}] {{}}\", msg)),",
238 variant.name,
239 ));
240 }
241
242 lines.push(" _ => ext_php_rs::exception::PhpException::default(msg),".to_string());
244 lines.push(" }".to_string());
245 lines.push("}".to_string());
246 lines.join("\n")
247}
248
249pub fn php_converter_fn_name(error: &ErrorDef) -> String {
251 format!("{}_to_php_err", to_snake_case(&error.name))
252}
253
254pub fn gen_magnus_error_converter(error: &ErrorDef, core_import: &str) -> String {
260 let rust_path = if error.rust_path.is_empty() {
261 format!("{core_import}::{}", error.name)
262 } else {
263 error.rust_path.replace('-', "_")
264 };
265
266 let fn_name = format!("{}_to_magnus_err", to_snake_case(&error.name));
267
268 let mut lines = Vec::new();
269 lines.push(format!("/// Convert a `{rust_path}` error to a Magnus runtime error."));
270 lines.push("#[allow(dead_code)]".to_string());
271 lines.push(format!("fn {fn_name}(e: {rust_path}) -> magnus::Error {{"));
272 lines.push(" let msg = e.to_string();".to_string());
273 lines.push(" magnus::Error::new(magnus::exception::runtime_error(), msg)".to_string());
274 lines.push("}".to_string());
275 lines.join("\n")
276}
277
278pub fn magnus_converter_fn_name(error: &ErrorDef) -> String {
280 format!("{}_to_magnus_err", to_snake_case(&error.name))
281}
282
283pub fn gen_rustler_error_converter(error: &ErrorDef, core_import: &str) -> String {
289 let rust_path = if error.rust_path.is_empty() {
290 format!("{core_import}::{}", error.name)
291 } else {
292 error.rust_path.replace('-', "_")
293 };
294
295 let fn_name = format!("{}_to_rustler_err", to_snake_case(&error.name));
296
297 let mut lines = Vec::new();
298 lines.push(format!("/// Convert a `{rust_path}` error to a Rustler error string."));
299 lines.push("#[allow(dead_code)]".to_string());
300 lines.push(format!("fn {fn_name}(e: {rust_path}) -> String {{"));
301 lines.push(" e.to_string()".to_string());
302 lines.push("}".to_string());
303 lines.join("\n")
304}
305
306pub fn rustler_converter_fn_name(error: &ErrorDef) -> String {
308 format!("{}_to_rustler_err", to_snake_case(&error.name))
309}
310
311pub fn gen_ffi_error_codes(error: &ErrorDef) -> String {
320 let prefix = to_screaming_snake(&error.name);
321 let prefix_lower = to_snake_case(&error.name);
322
323 let mut lines = Vec::new();
324 lines.push(format!("/// Error codes for `{}`.", error.name));
325 lines.push("typedef enum {".to_string());
326 lines.push(format!(" {}_NONE = 0,", prefix));
327
328 for (i, variant) in error.variants.iter().enumerate() {
329 let variant_screaming = to_screaming_snake(&variant.name);
330 lines.push(format!(" {}_{} = {},", prefix, variant_screaming, i + 1));
331 }
332
333 lines.push(format!("}} {}_t;\n", prefix_lower));
334
335 lines.push(format!(
337 "/// Return a static string describing the error code.\nconst char* {}_error_message({}_t code);",
338 prefix_lower, prefix_lower
339 ));
340
341 lines.join("\n")
342}
343
344pub fn gen_go_error_types(error: &ErrorDef) -> String {
350 let mut lines = Vec::new();
351
352 lines.push("var (".to_string());
354 for variant in &error.variants {
355 let err_name = format!("Err{}", variant.name);
356 let msg = variant_display_message(variant);
357 lines.push(format!(" {} = errors.New(\"{}\")", err_name, msg));
358 }
359 lines.push(")\n".to_string());
360
361 lines.push(format!("// {} is a structured error type.", error.name));
363 lines.push(format!("type {} struct {{", error.name));
364 lines.push(" Code string".to_string());
365 lines.push(" Message string".to_string());
366 lines.push("}\n".to_string());
367
368 lines.push(format!(
369 "func (e *{}) Error() string {{ return e.Message }}",
370 error.name
371 ));
372
373 lines.join("\n")
374}
375
376pub fn gen_java_error_types(error: &ErrorDef, package: &str) -> Vec<(String, String)> {
386 let mut files = Vec::with_capacity(error.variants.len() + 1);
387
388 let base_name = format!("{}Exception", error.name);
390 let mut base = String::with_capacity(512);
391 base.push_str(&format!(
392 "// DO NOT EDIT - auto-generated by alef\npackage {};\n\n",
393 package
394 ));
395 if !error.doc.is_empty() {
396 base.push_str(&format!("/** {} */\n", error.doc));
397 }
398 base.push_str(&format!("public class {} extends Exception {{\n", base_name));
399 base.push_str(&format!(
400 " public {}(String message) {{\n super(message);\n }}\n\n",
401 base_name
402 ));
403 base.push_str(&format!(
404 " public {}(String message, Throwable cause) {{\n super(message, cause);\n }}\n",
405 base_name
406 ));
407 base.push_str("}\n");
408 files.push((base_name.clone(), base));
409
410 for variant in &error.variants {
412 let class_name = format!("{}Exception", variant.name);
413 let mut content = String::with_capacity(512);
414 content.push_str(&format!(
415 "// DO NOT EDIT - auto-generated by alef\npackage {};\n\n",
416 package
417 ));
418 if !variant.doc.is_empty() {
419 content.push_str(&format!("/** {} */\n", variant.doc));
420 }
421 content.push_str(&format!("public class {} extends {} {{\n", class_name, base_name));
422 content.push_str(&format!(
423 " public {}(String message) {{\n super(message);\n }}\n\n",
424 class_name
425 ));
426 content.push_str(&format!(
427 " public {}(String message, Throwable cause) {{\n super(message, cause);\n }}\n",
428 class_name
429 ));
430 content.push_str("}\n");
431 files.push((class_name, content));
432 }
433
434 files
435}
436
437pub fn gen_csharp_error_types(error: &ErrorDef, namespace: &str) -> Vec<(String, String)> {
447 let mut files = Vec::with_capacity(error.variants.len() + 1);
448
449 let base_name = format!("{}Exception", error.name);
450
451 {
453 let mut out = String::with_capacity(512);
454 out.push_str("// This file is auto-generated by alef. DO NOT EDIT.\nusing System;\n\n");
455 out.push_str(&format!("namespace {};\n\n", namespace));
456 if !error.doc.is_empty() {
457 out.push_str("/// <summary>\n");
458 for line in error.doc.lines() {
459 out.push_str(&format!("/// {}\n", line));
460 }
461 out.push_str("/// </summary>\n");
462 }
463 out.push_str(&format!("public class {} : Exception\n{{\n", base_name));
464 out.push_str(&format!(
465 " public {}(string message) : base(message) {{ }}\n\n",
466 base_name
467 ));
468 out.push_str(&format!(
469 " public {}(string message, Exception innerException) : base(message, innerException) {{ }}\n",
470 base_name
471 ));
472 out.push_str("}\n");
473 files.push((base_name.clone(), out));
474 }
475
476 for variant in &error.variants {
478 let class_name = format!("{}Exception", variant.name);
479 let mut out = String::with_capacity(512);
480 out.push_str("// This file is auto-generated by alef. DO NOT EDIT.\nusing System;\n\n");
481 out.push_str(&format!("namespace {};\n\n", namespace));
482 if !variant.doc.is_empty() {
483 out.push_str("/// <summary>\n");
484 for line in variant.doc.lines() {
485 out.push_str(&format!("/// {}\n", line));
486 }
487 out.push_str("/// </summary>\n");
488 }
489 out.push_str(&format!("public class {} : {}\n{{\n", class_name, base_name));
490 out.push_str(&format!(
491 " public {}(string message) : base(message) {{ }}\n\n",
492 class_name
493 ));
494 out.push_str(&format!(
495 " public {}(string message, Exception innerException) : base(message, innerException) {{ }}\n",
496 class_name
497 ));
498 out.push_str("}\n");
499 files.push((class_name, out));
500 }
501
502 files
503}
504
505fn to_screaming_snake(s: &str) -> String {
511 let mut result = String::with_capacity(s.len() + 4);
512 for (i, c) in s.chars().enumerate() {
513 if c.is_uppercase() {
514 if i > 0 {
515 result.push('_');
516 }
517 result.push(c.to_ascii_uppercase());
518 } else {
519 result.push(c.to_ascii_uppercase());
520 }
521 }
522 result
523}
524
525fn variant_display_message(variant: &ErrorVariant) -> String {
530 if let Some(tmpl) = &variant.message_template {
531 let msg = tmpl
533 .replace("{0}", "")
534 .replace("{source}", "")
535 .trim_end_matches(": ")
536 .trim()
537 .to_string();
538 if msg.is_empty() {
539 to_snake_case(&variant.name).replace('_', " ")
540 } else {
541 msg
542 }
543 } else {
544 to_snake_case(&variant.name).replace('_', " ")
545 }
546}
547
548#[cfg(test)]
549mod tests {
550 use super::*;
551 use alef_core::ir::{ErrorDef, ErrorVariant};
552
553 fn sample_error() -> ErrorDef {
554 ErrorDef {
555 name: "ConversionError".to_string(),
556 rust_path: "html_to_markdown_rs::ConversionError".to_string(),
557 variants: vec![
558 ErrorVariant {
559 name: "ParseError".to_string(),
560 message_template: Some("HTML parsing error: {0}".to_string()),
561 fields: vec![],
562 has_source: false,
563 has_from: false,
564 is_unit: false,
565 doc: String::new(),
566 },
567 ErrorVariant {
568 name: "IoError".to_string(),
569 message_template: Some("I/O error: {0}".to_string()),
570 fields: vec![],
571 has_source: false,
572 has_from: true,
573 is_unit: false,
574 doc: String::new(),
575 },
576 ErrorVariant {
577 name: "Other".to_string(),
578 message_template: Some("Conversion error: {0}".to_string()),
579 fields: vec![],
580 has_source: false,
581 has_from: false,
582 is_unit: false,
583 doc: String::new(),
584 },
585 ],
586 doc: "Error type for conversion operations.".to_string(),
587 }
588 }
589
590 #[test]
591 fn test_gen_error_types() {
592 let error = sample_error();
593 let output = gen_pyo3_error_types(&error, "_module");
594 assert!(output.contains("pyo3::create_exception!(_module, ParseError, pyo3::exceptions::PyException);"));
595 assert!(output.contains("pyo3::create_exception!(_module, IoError, pyo3::exceptions::PyException);"));
596 assert!(output.contains("pyo3::create_exception!(_module, OtherError, pyo3::exceptions::PyException);"));
597 assert!(output.contains("pyo3::create_exception!(_module, ConversionError, pyo3::exceptions::PyException);"));
598 }
599
600 #[test]
601 fn test_gen_error_converter() {
602 let error = sample_error();
603 let output = gen_pyo3_error_converter(&error, "html_to_markdown_rs");
604 assert!(
605 output.contains("fn conversion_error_to_py_err(e: html_to_markdown_rs::ConversionError) -> pyo3::PyErr {")
606 );
607 assert!(output.contains("html_to_markdown_rs::ConversionError::ParseError(..) => ParseError::new_err(msg),"));
608 assert!(output.contains("html_to_markdown_rs::ConversionError::IoError(..) => IoError::new_err(msg),"));
609 }
610
611 #[test]
612 fn test_gen_error_registration() {
613 let error = sample_error();
614 let regs = gen_pyo3_error_registration(&error);
615 assert_eq!(regs.len(), 4); assert!(regs[0].contains("\"ParseError\""));
617 assert!(regs[3].contains("\"ConversionError\""));
618 }
619
620 #[test]
621 fn test_unit_variant_pattern() {
622 let error = ErrorDef {
623 name: "MyError".to_string(),
624 rust_path: "my_crate::MyError".to_string(),
625 variants: vec![ErrorVariant {
626 name: "NotFound".to_string(),
627 message_template: Some("not found".to_string()),
628 fields: vec![],
629 has_source: false,
630 has_from: false,
631 is_unit: true,
632 doc: String::new(),
633 }],
634 doc: String::new(),
635 };
636 let output = gen_pyo3_error_converter(&error, "my_crate");
637 assert!(output.contains("my_crate::MyError::NotFound => NotFoundError::new_err(msg),"));
638 assert!(!output.contains("NotFound(..)"));
640 }
641
642 #[test]
647 fn test_gen_napi_error_types() {
648 let error = sample_error();
649 let output = gen_napi_error_types(&error);
650 assert!(output.contains("CONVERSION_ERROR_ERROR_PARSE_ERROR"));
651 assert!(output.contains("CONVERSION_ERROR_ERROR_IO_ERROR"));
652 assert!(output.contains("CONVERSION_ERROR_ERROR_OTHER"));
653 }
654
655 #[test]
656 fn test_gen_napi_error_converter() {
657 let error = sample_error();
658 let output = gen_napi_error_converter(&error, "html_to_markdown_rs");
659 assert!(
660 output
661 .contains("fn conversion_error_to_napi_err(e: html_to_markdown_rs::ConversionError) -> napi::Error {")
662 );
663 assert!(output.contains("napi::Error::new(napi::Status::GenericFailure,"));
664 assert!(output.contains("[ParseError]"));
665 assert!(output.contains("[IoError]"));
666 assert!(output.contains("#[allow(dead_code)]"));
667 }
668
669 #[test]
670 fn test_napi_unit_variant() {
671 let error = ErrorDef {
672 name: "MyError".to_string(),
673 rust_path: "my_crate::MyError".to_string(),
674 variants: vec![ErrorVariant {
675 name: "NotFound".to_string(),
676 message_template: None,
677 fields: vec![],
678 has_source: false,
679 has_from: false,
680 is_unit: true,
681 doc: String::new(),
682 }],
683 doc: String::new(),
684 };
685 let output = gen_napi_error_converter(&error, "my_crate");
686 assert!(output.contains("my_crate::MyError::NotFound =>"));
687 assert!(!output.contains("NotFound(..)"));
688 }
689
690 #[test]
695 fn test_gen_wasm_error_converter() {
696 let error = sample_error();
697 let output = gen_wasm_error_converter(&error, "html_to_markdown_rs");
698 assert!(output.contains(
699 "fn conversion_error_to_js_value(e: html_to_markdown_rs::ConversionError) -> wasm_bindgen::JsValue {"
700 ));
701 assert!(output.contains("JsValue::from_str(&e.to_string())"));
702 assert!(output.contains("#[allow(dead_code)]"));
703 }
704
705 #[test]
710 fn test_gen_php_error_converter() {
711 let error = sample_error();
712 let output = gen_php_error_converter(&error, "html_to_markdown_rs");
713 assert!(output.contains("fn conversion_error_to_php_err(e: html_to_markdown_rs::ConversionError) -> ext_php_rs::exception::PhpException {"));
714 assert!(output.contains("PhpException::default(format!(\"[ParseError] {}\", msg))"));
715 assert!(output.contains("#[allow(dead_code)]"));
716 }
717
718 #[test]
723 fn test_gen_magnus_error_converter() {
724 let error = sample_error();
725 let output = gen_magnus_error_converter(&error, "html_to_markdown_rs");
726 assert!(
727 output.contains(
728 "fn conversion_error_to_magnus_err(e: html_to_markdown_rs::ConversionError) -> magnus::Error {"
729 )
730 );
731 assert!(output.contains("magnus::Error::new(magnus::exception::runtime_error(), msg)"));
732 assert!(output.contains("#[allow(dead_code)]"));
733 }
734
735 #[test]
740 fn test_gen_rustler_error_converter() {
741 let error = sample_error();
742 let output = gen_rustler_error_converter(&error, "html_to_markdown_rs");
743 assert!(
744 output.contains("fn conversion_error_to_rustler_err(e: html_to_markdown_rs::ConversionError) -> String {")
745 );
746 assert!(output.contains("e.to_string()"));
747 assert!(output.contains("#[allow(dead_code)]"));
748 }
749
750 #[test]
755 fn test_to_screaming_snake() {
756 assert_eq!(to_screaming_snake("ConversionError"), "CONVERSION_ERROR");
757 assert_eq!(to_screaming_snake("IoError"), "IO_ERROR");
758 assert_eq!(to_screaming_snake("Other"), "OTHER");
759 }
760
761 #[test]
766 fn test_gen_ffi_error_codes() {
767 let error = sample_error();
768 let output = gen_ffi_error_codes(&error);
769 assert!(output.contains("CONVERSION_ERROR_NONE = 0"));
770 assert!(output.contains("CONVERSION_ERROR_PARSE_ERROR = 1"));
771 assert!(output.contains("CONVERSION_ERROR_IO_ERROR = 2"));
772 assert!(output.contains("CONVERSION_ERROR_OTHER = 3"));
773 assert!(output.contains("conversion_error_t;"));
774 assert!(output.contains("conversion_error_error_message(conversion_error_t code)"));
775 }
776
777 #[test]
782 fn test_gen_go_error_types() {
783 let error = sample_error();
784 let output = gen_go_error_types(&error);
785 assert!(output.contains("ErrParseError = errors.New("));
786 assert!(output.contains("ErrIoError = errors.New("));
787 assert!(output.contains("ErrOther = errors.New("));
788 assert!(output.contains("type ConversionError struct {"));
789 assert!(output.contains("Code string"));
790 assert!(output.contains("func (e *ConversionError) Error() string"));
791 }
792
793 #[test]
798 fn test_gen_java_error_types() {
799 let error = sample_error();
800 let files = gen_java_error_types(&error, "dev.kreuzberg.test");
801 assert_eq!(files.len(), 4);
803 assert_eq!(files[0].0, "ConversionErrorException");
805 assert!(
806 files[0]
807 .1
808 .contains("public class ConversionErrorException extends Exception")
809 );
810 assert!(files[0].1.contains("package dev.kreuzberg.test;"));
811 assert_eq!(files[1].0, "ParseErrorException");
813 assert!(
814 files[1]
815 .1
816 .contains("public class ParseErrorException extends ConversionErrorException")
817 );
818 assert_eq!(files[2].0, "IoErrorException");
819 assert_eq!(files[3].0, "OtherException");
820 }
821
822 #[test]
827 fn test_gen_csharp_error_types() {
828 let error = sample_error();
829 let files = gen_csharp_error_types(&error, "Kreuzberg.Test");
830 assert_eq!(files.len(), 4);
832 assert_eq!(files[0].0, "ConversionErrorException");
834 assert!(files[0].1.contains("public class ConversionErrorException : Exception"));
835 assert!(files[0].1.contains("namespace Kreuzberg.Test;"));
836 assert_eq!(files[1].0, "ParseErrorException");
838 assert!(
839 files[1]
840 .1
841 .contains("public class ParseErrorException : ConversionErrorException")
842 );
843 assert_eq!(files[2].0, "IoErrorException");
844 assert_eq!(files[3].0, "OtherException");
845 }
846}