1use crate::error::ValidationError;
21use crate::scenario_config::{
22 ScenarioConfig, find_scenario_by_name_with_config, find_scenario_for_message_type_with_config,
23};
24use crate::xml::to_mx_xml;
25use datafake_rs::DataGenerator;
26use serde_json::{Value, json};
27
28type Result<T> = std::result::Result<T, ValidationError>;
29
30pub fn generate_sample_xml(
57 message_type: &str,
58 scenario_name: Option<&str>,
59 config: &ScenarioConfig,
60) -> Result<String> {
61 let scenario_json = if let Some(name) = scenario_name {
63 find_scenario_by_name_with_config(message_type, name, config)?
64 } else {
65 find_scenario_for_message_type_with_config(message_type, config)?
66 };
67
68 let envelope_data = generate_envelope_from_scenario(&scenario_json)?;
70
71 envelope_to_xml(envelope_data, message_type)
73}
74
75pub fn generate_sample_object<T>(
100 message_type: &str,
101 scenario_name: Option<&str>,
102 config: &ScenarioConfig,
103) -> Result<T>
104where
105 T: serde::de::DeserializeOwned,
106{
107 let scenario_json = if let Some(name) = scenario_name {
109 find_scenario_by_name_with_config(message_type, name, config)?
110 } else {
111 find_scenario_for_message_type_with_config(message_type, config)?
112 };
113
114 let envelope_data = generate_envelope_from_scenario(&scenario_json)?;
116
117 let document_data = extract_document_from_envelope(&envelope_data, message_type)?;
119
120 let generated_json = serde_json::to_string_pretty(&document_data).map_err(|e| {
122 ValidationError::new(9997, format!("Failed to serialize generated data: {e}"))
123 })?;
124
125 serde_json::from_str(&generated_json).map_err(|e| {
127 ValidationError::new(
128 9997,
129 format!("Failed to parse generated JSON into {message_type}: {e}"),
130 )
131 })
132}
133
134fn generate_envelope_from_scenario(scenario: &Value) -> Result<Value> {
140 let datafake_config = convert_to_datafake_config(scenario)?;
142
143 let generator = DataGenerator::from_value(datafake_config)
145 .map_err(|e| ValidationError::new(9997, format!("Failed to create generator: {e}")))?;
146
147 generator
149 .generate()
150 .map_err(|e| ValidationError::new(9997, format!("Failed to generate data: {e}")))
151}
152
153fn convert_to_datafake_config(scenario: &Value) -> Result<Value> {
155 let variables = scenario.get("variables").ok_or_else(|| {
157 ValidationError::new(9997, "Scenario missing 'variables' section".to_string())
158 })?;
159
160 let schema = scenario.get("schema").ok_or_else(|| {
161 ValidationError::new(9997, "Scenario missing 'schema' section".to_string())
162 })?;
163
164 let converted_variables = convert_variables(variables)?;
167
168 let converted_schema = convert_schema(schema)?;
170
171 Ok(json!({
172 "variables": converted_variables,
173 "schema": converted_schema
174 }))
175}
176
177fn convert_variables(variables: &Value) -> Result<Value> {
179 match variables {
180 Value::Object(vars) => {
181 let mut converted = serde_json::Map::new();
182
183 for (key, spec) in vars {
184 let converted_spec = convert_variable_spec(spec)?;
185 converted.insert(key.clone(), converted_spec);
186 }
187
188 Ok(Value::Object(converted))
189 }
190 _ => Ok(variables.clone()),
191 }
192}
193
194fn convert_variable_spec(spec: &Value) -> Result<Value> {
196 match spec {
197 Value::Object(obj) => {
198 if obj.contains_key("cat") {
200 return Ok(spec.clone());
201 }
202
203 if let Some(fake_spec) = obj.get("fake") {
205 if let Value::Array(fake_arr) = fake_spec {
206 if let Some(Value::String(fake_type)) = fake_arr.first() {
208 let mapped_spec = map_fake_type(fake_type, &fake_arr[1..])?;
209 return Ok(mapped_spec);
210 }
211 }
212 return Ok(spec.clone());
213 }
214
215 if let Some(Value::Array(options)) = obj.get("pick") {
217 let mut fake_array = vec![json!("enum")];
219 for option in options {
220 fake_array.push(option.clone());
221 }
222 return Ok(json!({"fake": fake_array}));
223 }
224
225 Ok(spec.clone())
226 }
227 Value::String(_) | Value::Number(_) | Value::Bool(_) | Value::Null => {
228 Ok(spec.clone())
230 }
231 _ => Ok(spec.clone()),
232 }
233}
234
235fn map_fake_type(fake_type: &str, args: &[Value]) -> Result<Value> {
237 match fake_type {
239 "iso8601_datetime" => {
241 let now = chrono::Utc::now();
243 Ok(json!(now.to_rfc3339()))
244 }
245 "currency_code" => {
247 Ok(json!({"fake": ["enum", "EUR", "USD", "GBP", "CHF", "JPY", "CAD", "AUD"]}))
249 }
250 "iban" => {
252 let country = args.first().and_then(|v| v.as_str()).unwrap_or("DE");
253 Ok(json!({"fake": ["iban", country]}))
254 }
255 "bic" => Ok(json!({"fake": ["bic"]})),
257 "lei" => Ok(json!({"fake": ["lei"]})),
259 "alphanumeric" => {
261 let min_len = args.first().and_then(|v| v.as_u64()).unwrap_or(10);
262 let max_len = args.get(1).and_then(|v| v.as_u64()).unwrap_or(min_len);
263 Ok(json!({"fake": ["alphanumeric", min_len, max_len]}))
264 }
265 "words" => {
267 let min = args.first().and_then(|v| v.as_u64()).unwrap_or(1);
268 let max = args.get(1).and_then(|v| v.as_u64()).unwrap_or(3);
269 Ok(json!({"fake": ["words", min, max]}))
270 }
271 "regex" => {
273 let mut fake_array = vec![json!("regex")];
274 for arg in args {
275 fake_array.push(arg.clone());
276 }
277 Ok(json!({"fake": fake_array}))
278 }
279 _ => {
281 let mut fake_array = vec![json!(fake_type)];
282 for arg in args {
283 fake_array.push(arg.clone());
284 }
285 Ok(json!({"fake": fake_array}))
286 }
287 }
288}
289
290fn convert_schema(schema: &Value) -> Result<Value> {
292 process_schema_value(schema)
293}
294
295fn process_schema_value(value: &Value) -> Result<Value> {
297 match value {
298 Value::Object(obj) => {
299 let mut result = serde_json::Map::new();
300
301 for (key, val) in obj {
302 if key == "@Ccy" {
304 result.insert(key.clone(), process_schema_value(val)?);
306 } else if key == "$value" {
307 result.insert(key.clone(), process_schema_value(val)?);
309 } else if key == "var" {
310 return Ok(value.clone());
312 } else if key == "fake" || key == "cat" || key == "pick" {
313 return convert_variable_spec(value);
315 } else {
316 result.insert(key.clone(), process_schema_value(val)?);
318 }
319 }
320
321 Ok(Value::Object(result))
322 }
323 Value::Array(arr) => {
324 let mut result = Vec::new();
325 for item in arr {
326 result.push(process_schema_value(item)?);
327 }
328 Ok(Value::Array(result))
329 }
330 _ => Ok(value.clone()),
331 }
332}
333
334fn extract_document_from_envelope(envelope: &Value, message_type: &str) -> Result<Value> {
336 let document = envelope.get("Document").ok_or_else(|| {
337 ValidationError::new(9997, "Envelope missing 'Document' section".to_string())
338 })?;
339
340 let doc_root = get_document_root_element(message_type);
342
343 document
344 .get(&doc_root)
345 .cloned()
346 .ok_or_else(|| ValidationError::new(9997, format!("Document missing '{doc_root}' element")))
347}
348
349fn get_document_root_element(message_type: &str) -> String {
351 match message_type {
352 "pacs008" => "FIToFICstmrCdtTrf",
353 "pacs009" => "FinInstnCdtTrf",
354 "pacs003" => "FIToFICstmrDrctDbt",
355 "pacs004" => "PmtRtr",
356 "pacs002" => "FIToFIPmtStsRpt",
357 "pain001" => "CstmrCdtTrfInitn",
358 "pain008" => "CstmrDrctDbtInitn",
359 "camt025" => "Rcpt",
360 "camt029" => "RsltnOfInvstgtn",
361 "camt052" => "BkToCstmrAcctRpt",
362 "camt053" => "BkToCstmrStmt",
363 "camt054" => "BkToCstmrDbtCdtNtfctn",
364 "camt056" => "FIToFIPmtCxlReq",
365 "camt057" => "NtfctnToRcv",
366 "camt060" => "AcctRptgReq",
367 "camt107" => "ChqPresntmntNtfctn",
368 "camt108" => "ChqCxlOrStopReq",
369 "camt109" => "ChqCxlOrStopRpt",
370 "pacs010" => "FIDrctDbt",
371 _ => "Document", }
373 .to_string()
374}
375
376fn envelope_to_xml(envelope: Value, message_type: &str) -> Result<String> {
378 match message_type {
380 "pacs008" => {
381 use crate::document::pacs_008_001_08::FIToFICustomerCreditTransferV08;
382 use crate::header::bah_pacs_008_001_08::BusinessApplicationHeaderV02;
383
384 let header: BusinessApplicationHeaderV02 =
385 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
386 ValidationError::new(9997, format!("Failed to parse pacs008 header: {e}"))
387 })?;
388
389 let message: FIToFICustomerCreditTransferV08 = serde_json::from_value(
390 envelope["Document"]["FIToFICstmrCdtTrf"].clone(),
391 )
392 .map_err(|e| {
393 ValidationError::new(9997, format!("Failed to parse pacs008 document: {e}"))
394 })?;
395
396 to_mx_xml(&message, header, "pacs.008", None)
397 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
398 }
399 "pacs009" => {
400 use crate::document::pacs_009_001_08::FinancialInstitutionCreditTransferV08;
401 use crate::header::bah_pacs_009_001_08::BusinessApplicationHeaderV02;
402
403 let header: BusinessApplicationHeaderV02 =
404 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
405 ValidationError::new(9997, format!("Failed to parse pacs009 header: {e}"))
406 })?;
407
408 let message: FinancialInstitutionCreditTransferV08 = serde_json::from_value(
409 envelope["Document"]["FinInstnCdtTrf"].clone(),
410 )
411 .map_err(|e| {
412 ValidationError::new(9997, format!("Failed to parse pacs009 document: {e}"))
413 })?;
414
415 to_mx_xml(&message, header, "pacs.009", None)
416 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
417 }
418 "pacs003" => {
419 use crate::document::pacs_003_001_08::FIToFICustomerDirectDebitV08;
420 use crate::header::bah_pacs_003_001_08::BusinessApplicationHeaderV02;
421
422 let header: BusinessApplicationHeaderV02 =
423 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
424 ValidationError::new(9997, format!("Failed to parse pacs003 header: {e}"))
425 })?;
426
427 let message: FIToFICustomerDirectDebitV08 =
428 serde_json::from_value(envelope["Document"]["FIToFICstmrDrctDbt"].clone())
429 .map_err(|e| {
430 ValidationError::new(9997, format!("Failed to parse pacs003 document: {e}"))
431 })?;
432
433 to_mx_xml(&message, header, "pacs.003", None)
434 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
435 }
436 "pacs004" => {
437 use crate::document::pacs_004_001_09::PaymentReturnV09;
438 use crate::header::bah_pacs_004_001_09::BusinessApplicationHeaderV02;
439
440 let header: BusinessApplicationHeaderV02 =
441 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
442 ValidationError::new(9997, format!("Failed to parse pacs004 header: {e}"))
443 })?;
444
445 let message: PaymentReturnV09 =
446 serde_json::from_value(envelope["Document"]["PmtRtr"].clone()).map_err(|e| {
447 ValidationError::new(9997, format!("Failed to parse pacs004 document: {e}"))
448 })?;
449
450 to_mx_xml(&message, header, "pacs.004", None)
451 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
452 }
453 "pacs010" => {
454 use crate::document::pacs_010_001_03::FinancialInstitutionDirectDebitV03;
455 use crate::header::bah_pacs_010_001_03::BusinessApplicationHeaderV02;
456
457 let header: BusinessApplicationHeaderV02 =
458 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
459 ValidationError::new(9997, format!("Failed to parse pacs010 header: {e}"))
460 })?;
461
462 let message: FinancialInstitutionDirectDebitV03 =
463 serde_json::from_value(envelope["Document"]["FIDrctDbt"].clone()).map_err(|e| {
464 ValidationError::new(9997, format!("Failed to parse pacs010 document: {e}"))
465 })?;
466
467 to_mx_xml(&message, header, "pacs.010", None)
468 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
469 }
470 "pacs002" => {
471 use crate::document::pacs_002_001_10::FIToFIPaymentStatusReportV10;
472 use crate::header::bah_pacs_002_001_10::BusinessApplicationHeaderV02;
473
474 let header: BusinessApplicationHeaderV02 =
475 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
476 ValidationError::new(9997, format!("Failed to parse pacs002 header: {e}"))
477 })?;
478
479 let message: FIToFIPaymentStatusReportV10 = serde_json::from_value(
480 envelope["Document"]["FIToFIPmtStsRpt"].clone(),
481 )
482 .map_err(|e| {
483 ValidationError::new(9997, format!("Failed to parse pacs002 document: {e}"))
484 })?;
485
486 to_mx_xml(&message, header, "pacs.002", None)
487 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
488 }
489 "pain001" => {
490 use crate::document::pain_001_001_09::CustomerCreditTransferInitiationV09;
491 use crate::header::bah_pain_001_001_09::BusinessApplicationHeaderV02;
492
493 let header: BusinessApplicationHeaderV02 =
494 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
495 ValidationError::new(9997, format!("Failed to parse pain001 header: {e}"))
496 })?;
497
498 let message: CustomerCreditTransferInitiationV09 = serde_json::from_value(
499 envelope["Document"]["CstmrCdtTrfInitn"].clone(),
500 )
501 .map_err(|e| {
502 ValidationError::new(9997, format!("Failed to parse pain001 document: {e}"))
503 })?;
504
505 to_mx_xml(&message, header, "pain.001", None)
506 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
507 }
508 "pain008" => {
509 use crate::document::pain_008_001_08::CustomerDirectDebitInitiationV08;
510 use crate::header::bah_pain_008_001_08::BusinessApplicationHeaderV02;
511
512 let header: BusinessApplicationHeaderV02 =
513 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
514 ValidationError::new(9997, format!("Failed to parse pain008 header: {e}"))
515 })?;
516
517 let message: CustomerDirectDebitInitiationV08 = serde_json::from_value(
518 envelope["Document"]["CstmrDrctDbtInitn"].clone(),
519 )
520 .map_err(|e| {
521 ValidationError::new(9997, format!("Failed to parse pain008 document: {e}"))
522 })?;
523
524 to_mx_xml(&message, header, "pain.008", None)
525 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
526 }
527 "camt025" => {
528 use crate::document::camt_025_001_08::ReceiptV08;
529 use crate::header::bah_camt_025_001_08::BusinessApplicationHeaderV02;
530
531 let header: BusinessApplicationHeaderV02 =
532 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
533 ValidationError::new(9997, format!("Failed to parse camt025 header: {e}"))
534 })?;
535
536 let message: ReceiptV08 = serde_json::from_value(envelope["Document"]["Rcpt"].clone())
537 .map_err(|e| {
538 ValidationError::new(9997, format!("Failed to parse camt025 document: {e}"))
539 })?;
540
541 to_mx_xml(&message, header, "camt.025", None)
542 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
543 }
544 "camt029" => {
545 use crate::document::camt_029_001_09::ResolutionOfInvestigationV09;
546 use crate::header::bah_camt_029_001::BusinessApplicationHeaderV02;
547
548 let header: BusinessApplicationHeaderV02 =
549 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
550 ValidationError::new(9997, format!("Failed to parse camt029 header: {e}"))
551 })?;
552
553 let message: ResolutionOfInvestigationV09 = serde_json::from_value(
554 envelope["Document"]["RsltnOfInvstgtn"].clone(),
555 )
556 .map_err(|e| {
557 ValidationError::new(9997, format!("Failed to parse camt029 document: {e}"))
558 })?;
559
560 to_mx_xml(&message, header, "camt.029", None)
561 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
562 }
563 "camt052" => {
564 use crate::document::camt_052_001_08::BankToCustomerAccountReportV08;
565 use crate::header::bah_camt_052_001_08::BusinessApplicationHeaderV02;
566
567 let header: BusinessApplicationHeaderV02 =
568 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
569 ValidationError::new(9997, format!("Failed to parse camt052 header: {e}"))
570 })?;
571
572 let message: BankToCustomerAccountReportV08 = serde_json::from_value(
573 envelope["Document"]["BkToCstmrAcctRpt"].clone(),
574 )
575 .map_err(|e| {
576 ValidationError::new(9997, format!("Failed to parse camt052 document: {e}"))
577 })?;
578
579 to_mx_xml(&message, header, "camt.052", None)
580 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
581 }
582 "camt053" => {
583 use crate::document::camt_053_001_08::BankToCustomerStatementV08;
584 use crate::header::bah_camt_053_001_08::BusinessApplicationHeaderV02;
585
586 let header: BusinessApplicationHeaderV02 =
587 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
588 ValidationError::new(9997, format!("Failed to parse camt053 header: {e}"))
589 })?;
590
591 let message: BankToCustomerStatementV08 = serde_json::from_value(
592 envelope["Document"]["BkToCstmrStmt"].clone(),
593 )
594 .map_err(|e| {
595 ValidationError::new(9997, format!("Failed to parse camt053 document: {e}"))
596 })?;
597
598 to_mx_xml(&message, header, "camt.053", None)
599 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
600 }
601 "camt054" => {
602 use crate::document::camt_054_001_08::BankToCustomerDebitCreditNotificationV08;
603 use crate::header::bah_camt_054_001::BusinessApplicationHeaderV02;
604
605 let header: BusinessApplicationHeaderV02 =
606 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
607 ValidationError::new(9997, format!("Failed to parse camt054 header: {e}"))
608 })?;
609
610 let message: BankToCustomerDebitCreditNotificationV08 =
611 serde_json::from_value(envelope["Document"]["BkToCstmrDbtCdtNtfctn"].clone())
612 .map_err(|e| {
613 ValidationError::new(9997, format!("Failed to parse camt054 document: {e}"))
614 })?;
615
616 to_mx_xml(&message, header, "camt.054", None)
617 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
618 }
619 "camt056" => {
620 use crate::document::camt_056_001_08::FIToFIPaymentCancellationRequestV08;
621 use crate::header::bah_camt_056_001_08::BusinessApplicationHeaderV02;
622
623 let header: BusinessApplicationHeaderV02 =
624 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
625 ValidationError::new(9997, format!("Failed to parse camt056 header: {e}"))
626 })?;
627
628 let message: FIToFIPaymentCancellationRequestV08 = serde_json::from_value(
629 envelope["Document"]["FIToFIPmtCxlReq"].clone(),
630 )
631 .map_err(|e| {
632 ValidationError::new(9997, format!("Failed to parse camt056 document: {e}"))
633 })?;
634
635 to_mx_xml(&message, header, "camt.056", None)
636 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
637 }
638 "camt057" => {
639 use crate::document::camt_057_001_06::NotificationToReceiveV06;
640 use crate::header::bah_camt_057_001_06::BusinessApplicationHeaderV02;
641
642 let header: BusinessApplicationHeaderV02 =
643 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
644 ValidationError::new(9997, format!("Failed to parse camt057 header: {e}"))
645 })?;
646
647 let message: NotificationToReceiveV06 = serde_json::from_value(
648 envelope["Document"]["NtfctnToRcv"].clone(),
649 )
650 .map_err(|e| {
651 ValidationError::new(9997, format!("Failed to parse camt057 document: {e}"))
652 })?;
653
654 to_mx_xml(&message, header, "camt.057", None)
655 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
656 }
657 "camt060" => {
658 use crate::document::camt_060_001_05::AccountReportingRequestV05;
659 use crate::header::bah_camt_060_001_05::BusinessApplicationHeaderV02;
660
661 let header: BusinessApplicationHeaderV02 =
662 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
663 ValidationError::new(9997, format!("Failed to parse camt060 header: {e}"))
664 })?;
665
666 let message: AccountReportingRequestV05 = serde_json::from_value(
667 envelope["Document"]["AcctRptgReq"].clone(),
668 )
669 .map_err(|e| {
670 ValidationError::new(9997, format!("Failed to parse camt060 document: {e}"))
671 })?;
672
673 to_mx_xml(&message, header, "camt.060", None)
674 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
675 }
676 "camt107" => {
677 use crate::document::camt_107_001_01::ChequePresentmentNotificationV01;
678 use crate::header::bah_camt_107_001_01::BusinessApplicationHeaderV02;
679
680 let header: BusinessApplicationHeaderV02 =
681 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
682 ValidationError::new(9997, format!("Failed to parse camt107 header: {e}"))
683 })?;
684
685 let message: ChequePresentmentNotificationV01 =
686 serde_json::from_value(envelope["Document"]["ChqPresntmntNtfctn"].clone())
687 .map_err(|e| {
688 ValidationError::new(9997, format!("Failed to parse camt107 document: {e}"))
689 })?;
690
691 to_mx_xml(&message, header, "camt.107", None)
692 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
693 }
694 "camt108" => {
695 use crate::document::camt_108_001_01::ChequeCancellationOrStopRequestV01;
696 use crate::header::bah_camt_108_001_01::BusinessApplicationHeaderV02;
697
698 let header: BusinessApplicationHeaderV02 =
699 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
700 ValidationError::new(9997, format!("Failed to parse camt108 header: {e}"))
701 })?;
702
703 let message: ChequeCancellationOrStopRequestV01 = serde_json::from_value(
704 envelope["Document"]["ChqCxlOrStopReq"].clone(),
705 )
706 .map_err(|e| {
707 ValidationError::new(9997, format!("Failed to parse camt108 document: {e}"))
708 })?;
709
710 to_mx_xml(&message, header, "camt.108", None)
711 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
712 }
713 "camt109" => {
714 use crate::document::camt_109_001_01::ChequeCancellationOrStopReportV01;
715 use crate::header::bah_camt_109_001_01::BusinessApplicationHeaderV02;
716
717 let header: BusinessApplicationHeaderV02 =
718 serde_json::from_value(envelope["AppHdr"].clone()).map_err(|e| {
719 ValidationError::new(9997, format!("Failed to parse camt109 header: {e}"))
720 })?;
721
722 let message: ChequeCancellationOrStopReportV01 = serde_json::from_value(
723 envelope["Document"]["ChqCxlOrStopRpt"].clone(),
724 )
725 .map_err(|e| {
726 ValidationError::new(9997, format!("Failed to parse camt109 document: {e}"))
727 })?;
728
729 to_mx_xml(&message, header, "camt.109", None)
730 .map_err(|e| ValidationError::new(9997, format!("Failed to generate XML: {e}")))
731 }
732 _ => Err(ValidationError::new(
733 9997,
734 format!("Unsupported message type: {message_type}"),
735 )),
736 }
737}