1use crate::error::MxError;
6use crate::message_registry;
7use quick_xml::de::from_str as xml_from_str;
8
9pub type XmlError = MxError;
11
12pub fn from_mx_xml_envelope_str(xml: &str, message_type: &str) -> Result<(), XmlError> {
16 use crate::mx_envelope::MxMessage;
17
18 let has_envelope = xml.contains("<AppHdr") || xml.contains("<Envelope");
20
21 if !has_envelope {
22 MxMessage::from_xml(xml).map_err(|e| {
24 MxError::XmlDeserialization(format!(
25 "Failed to validate Document (message_type={}): {}",
26 message_type, e
27 ))
28 })?;
29 return Ok(());
30 }
31
32 xml_from_str::<MxMessage>(xml).map_err(|e| {
35 MxError::XmlDeserialization(format!("Failed to parse {} envelope: {}", message_type, e))
36 })?;
37
38 Ok(())
39}
40
41fn get_document_element_name(message_type: &str) -> &'static str {
44 message_registry::message_type_to_element(message_type).unwrap_or("Unknown")
45}
46
47macro_rules! xml_to_json_doc {
49 ($xml:expr, $path:path, $msg_type:expr) => {{
50 let doc = xml_from_str::<$path>($xml).map_err(|e| {
51 MxError::XmlDeserialization(format!("Failed to parse {}: {}", $msg_type, e))
52 })?;
53 serde_json::to_value(&doc).map_err(|e| MxError::XmlSerialization(e.to_string()))?
54 }};
55}
56
57pub fn xml_to_json_via_document(
61 xml: &str,
62 message_type: &str,
63) -> Result<serde_json::Value, XmlError> {
64 use crate::document::*;
65 use serde_json::json;
66
67 let inner_xml = xml
69 .trim()
70 .trim_start_matches("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
71 .trim();
72
73 let inner_xml = if let Some(start_idx) = inner_xml.find("<Document") {
75 if let Some(end_bracket) = inner_xml[start_idx..].find('>') {
76 let content_start = start_idx + end_bracket + 1;
77 if let Some(end_idx) = inner_xml.rfind("</Document>") {
78 &inner_xml[content_start..end_idx]
79 } else {
80 inner_xml
81 }
82 } else {
83 inner_xml
84 }
85 } else {
86 inner_xml
87 };
88
89 let inner_xml = inner_xml.trim();
90
91 let json_value = match message_type {
93 "pacs.008" => xml_to_json_doc!(
94 inner_xml,
95 pacs_008_001_08::FIToFICustomerCreditTransferV08,
96 "pacs.008"
97 ),
98 "pacs.009" => xml_to_json_doc!(
99 inner_xml,
100 pacs_009_001_08::FinancialInstitutionCreditTransferV08,
101 "pacs.009"
102 ),
103 "pacs.003" => xml_to_json_doc!(
104 inner_xml,
105 pacs_003_001_08::FIToFICustomerDirectDebitV08,
106 "pacs.003"
107 ),
108 "pacs.004" => xml_to_json_doc!(inner_xml, pacs_004_001_09::PaymentReturnV09, "pacs.004"),
109 "pacs.010" => xml_to_json_doc!(
110 inner_xml,
111 pacs_010_001_03::FinancialInstitutionDirectDebitV03,
112 "pacs.010"
113 ),
114 "pacs.002" => xml_to_json_doc!(
115 inner_xml,
116 pacs_002_001_10::FIToFIPaymentStatusReportV10,
117 "pacs.002"
118 ),
119 "pain.001" => xml_to_json_doc!(
120 inner_xml,
121 pain_001_001_09::CustomerCreditTransferInitiationV09,
122 "pain.001"
123 ),
124 "pain.008" => xml_to_json_doc!(
125 inner_xml,
126 pain_008_001_08::CustomerDirectDebitInitiationV08,
127 "pain.008"
128 ),
129 "camt.025" => xml_to_json_doc!(inner_xml, camt_025_001_08::ReceiptV08, "camt.025"),
130 "camt.029" => xml_to_json_doc!(
131 inner_xml,
132 camt_029_001_09::ResolutionOfInvestigationV09,
133 "camt.029"
134 ),
135 "camt.052" => xml_to_json_doc!(
136 inner_xml,
137 camt_052_001_08::BankToCustomerAccountReportV08,
138 "camt.052"
139 ),
140 "camt.053" => xml_to_json_doc!(
141 inner_xml,
142 camt_053_001_08::BankToCustomerStatementV08,
143 "camt.053"
144 ),
145 "camt.054" => xml_to_json_doc!(
146 inner_xml,
147 camt_054_001_08::BankToCustomerDebitCreditNotificationV08,
148 "camt.054"
149 ),
150 "camt.056" => xml_to_json_doc!(
151 inner_xml,
152 camt_056_001_08::FIToFIPaymentCancellationRequestV08,
153 "camt.056"
154 ),
155 "camt.057" => xml_to_json_doc!(
156 inner_xml,
157 camt_057_001_06::NotificationToReceiveV06,
158 "camt.057"
159 ),
160 "camt.060" => xml_to_json_doc!(
161 inner_xml,
162 camt_060_001_05::AccountReportingRequestV05,
163 "camt.060"
164 ),
165 "camt.107" => xml_to_json_doc!(
166 inner_xml,
167 camt_107_001_01::ChequePresentmentNotificationV01,
168 "camt.107"
169 ),
170 "camt.108" => xml_to_json_doc!(
171 inner_xml,
172 camt_108_001_01::ChequeCancellationOrStopRequestV01,
173 "camt.108"
174 ),
175 "camt.109" => xml_to_json_doc!(
176 inner_xml,
177 camt_109_001_01::ChequeCancellationOrStopReportV01,
178 "camt.109"
179 ),
180 "admi.024" => xml_to_json_doc!(
181 inner_xml,
182 admi_024_001_01::NotificationOfCorrespondenceV01,
183 "admi.024"
184 ),
185 _ => {
186 return Err(MxError::XmlDeserialization(format!(
187 "Unsupported message type: {}",
188 message_type
189 )));
190 }
191 };
192
193 let element_name = get_document_element_name(message_type);
195 Ok(json!({
196 "Document": {
197 element_name: json_value
198 }
199 }))
200}
201
202pub fn from_mx_xml_to_json(xml: &str) -> Result<serde_json::Value, XmlError> {
210 let value: serde_json::Value = quick_xml::de::from_str(xml)
213 .map_err(|e| MxError::XmlDeserialization(format!("XML parsing error: {}", e)))?;
214
215 Ok(value)
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_get_document_element_name() {
224 assert_eq!(get_document_element_name("pacs.008"), "FIToFICstmrCdtTrf");
225 assert_eq!(get_document_element_name("camt.053"), "BkToCstmrStmt");
226 assert_eq!(get_document_element_name("pain.001"), "CstmrCdtTrfInitn");
227 }
228}