mx_message/
message_registry.rs

1// Plasmatic MX Message Parsing Library
2// https://github.com/GoPlasmatic/MXMessage
3//
4// Copyright (c) 2025 Plasmatic
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17// You may obtain a copy of this library at
18// https://github.com/GoPlasmatic/MXMessage
19
20//! Message Type Registry
21//!
22//! Single source of truth for ISO20022 message type mappings.
23//! This module provides mappings between:
24//! - Short form message types (e.g., "pacs.008")
25//! - Full form message types (e.g., "pacs.008.001.08")
26//! - Rust struct names (e.g., "FIToFICustomerCreditTransferV08")
27//! - XML element names (e.g., "FIToFICstmrCdtTrf")
28//! - XML namespaces
29
30/// Message type registry entry
31/// Format: (short_form, full_form, rust_type_name, xml_element_name)
32pub struct MessageTypeInfo {
33    pub short_form: &'static str,
34    pub full_form: &'static str,
35    pub rust_type_name: &'static str,
36    pub xml_element_name: &'static str,
37}
38
39/// Complete registry of all supported ISO20022 message types
40pub const MESSAGE_REGISTRY: &[MessageTypeInfo] = &[
41    // PACS - Payment Clearing and Settlement
42    MessageTypeInfo {
43        short_form: "pacs.008",
44        full_form: "pacs.008.001.08",
45        rust_type_name: "FIToFICustomerCreditTransferV08",
46        xml_element_name: "FIToFICstmrCdtTrf",
47    },
48    MessageTypeInfo {
49        short_form: "pacs.002",
50        full_form: "pacs.002.001.10",
51        rust_type_name: "FIToFIPaymentStatusReportV10",
52        xml_element_name: "FIToFIPmtStsRpt",
53    },
54    MessageTypeInfo {
55        short_form: "pacs.003",
56        full_form: "pacs.003.001.08",
57        rust_type_name: "FIToFICustomerDirectDebitV08",
58        xml_element_name: "FIToFICstmrDrctDbt",
59    },
60    MessageTypeInfo {
61        short_form: "pacs.004",
62        full_form: "pacs.004.001.09",
63        rust_type_name: "PaymentReturnV09",
64        xml_element_name: "PmtRtr",
65    },
66    MessageTypeInfo {
67        short_form: "pacs.009",
68        full_form: "pacs.009.001.08",
69        rust_type_name: "FinancialInstitutionCreditTransferV08",
70        xml_element_name: "FICdtTrf",
71    },
72    MessageTypeInfo {
73        short_form: "pacs.010",
74        full_form: "pacs.010.001.03",
75        rust_type_name: "FinancialInstitutionDirectDebitV03",
76        xml_element_name: "FIDrctDbt",
77    },
78    // PAIN - Payment Initiation
79    MessageTypeInfo {
80        short_form: "pain.001",
81        full_form: "pain.001.001.09",
82        rust_type_name: "CustomerCreditTransferInitiationV09",
83        xml_element_name: "CstmrCdtTrfInitn",
84    },
85    MessageTypeInfo {
86        short_form: "pain.002",
87        full_form: "pain.002.001.10",
88        rust_type_name: "CustomerPaymentStatusReportV10",
89        xml_element_name: "CstmrPmtStsRpt",
90    },
91    MessageTypeInfo {
92        short_form: "pain.008",
93        full_form: "pain.008.001.08",
94        rust_type_name: "CustomerDirectDebitInitiationV08",
95        xml_element_name: "CstmrDrctDbtInitn",
96    },
97    // CAMT - Cash Management
98    MessageTypeInfo {
99        short_form: "camt.025",
100        full_form: "camt.025.001.08",
101        rust_type_name: "ReceiptV08",
102        xml_element_name: "Rcpt",
103    },
104    MessageTypeInfo {
105        short_form: "camt.029",
106        full_form: "camt.029.001.09",
107        rust_type_name: "ResolutionOfInvestigationV09",
108        xml_element_name: "RsltnOfInvstgtn",
109    },
110    MessageTypeInfo {
111        short_form: "camt.052",
112        full_form: "camt.052.001.08",
113        rust_type_name: "BankToCustomerAccountReportV08",
114        xml_element_name: "BkToCstmrAcctRpt",
115    },
116    MessageTypeInfo {
117        short_form: "camt.053",
118        full_form: "camt.053.001.08",
119        rust_type_name: "BankToCustomerStatementV08",
120        xml_element_name: "BkToCstmrStmt",
121    },
122    MessageTypeInfo {
123        short_form: "camt.054",
124        full_form: "camt.054.001.08",
125        rust_type_name: "BankToCustomerDebitCreditNotificationV08",
126        xml_element_name: "BkToCstmrDbtCdtNtfctn",
127    },
128    MessageTypeInfo {
129        short_form: "camt.055",
130        full_form: "camt.055.001.08",
131        rust_type_name: "CustomerPaymentCancellationRequestV08",
132        xml_element_name: "CstmrPmtCxlReq",
133    },
134    MessageTypeInfo {
135        short_form: "camt.056",
136        full_form: "camt.056.001.08",
137        rust_type_name: "FIToFIPaymentCancellationRequestV08",
138        xml_element_name: "FIToFIPmtCxlReq",
139    },
140    MessageTypeInfo {
141        short_form: "camt.058",
142        full_form: "camt.058.001.08",
143        rust_type_name: "NotificationToReceiveCancellationAdviceV08",
144        xml_element_name: "NtfctnToRcvCxlAdvc",
145    },
146    MessageTypeInfo {
147        short_form: "camt.057",
148        full_form: "camt.057.001.06",
149        rust_type_name: "NotificationToReceiveV06",
150        xml_element_name: "NtfctnToRcv",
151    },
152    MessageTypeInfo {
153        short_form: "camt.060",
154        full_form: "camt.060.001.05",
155        rust_type_name: "AccountReportingRequestV05",
156        xml_element_name: "AcctRptgReq",
157    },
158    MessageTypeInfo {
159        short_form: "camt.105",
160        full_form: "camt.105.001.02",
161        rust_type_name: "ChargesPaymentNotificationV02",
162        xml_element_name: "ChrgsPmtNtfctn",
163    },
164    MessageTypeInfo {
165        short_form: "camt.106",
166        full_form: "camt.106.001.02",
167        rust_type_name: "ChargesPaymentRequestV02",
168        xml_element_name: "ChrgsPmtReq",
169    },
170    MessageTypeInfo {
171        short_form: "camt.107",
172        full_form: "camt.107.001.01",
173        rust_type_name: "ChequePresentmentNotificationV01",
174        xml_element_name: "ChqPresntmntNtfctn",
175    },
176    MessageTypeInfo {
177        short_form: "camt.108",
178        full_form: "camt.108.001.01",
179        rust_type_name: "ChequeCancellationOrStopRequestV01",
180        xml_element_name: "ChqCxlOrStopReq",
181    },
182    MessageTypeInfo {
183        short_form: "camt.109",
184        full_form: "camt.109.001.01",
185        rust_type_name: "ChequeCancellationOrStopReportV01",
186        xml_element_name: "ChqCxlOrStopRpt",
187    },
188    // ADMI - Administration
189    MessageTypeInfo {
190        short_form: "admi.024",
191        full_form: "admi.024.001.01",
192        rust_type_name: "NotificationOfCorrespondenceV01",
193        xml_element_name: "NtfctnOfCrspdc",
194    },
195];
196
197/// Get namespace URI for a message type
198pub fn get_namespace(message_type: &str) -> String {
199    // Look up in the registry
200    for info in MESSAGE_REGISTRY {
201        if message_type == info.short_form || message_type == info.full_form {
202            return format!("urn:iso:std:iso:20022:tech:xsd:{}", info.full_form);
203        }
204    }
205
206    // Default fallback: construct namespace from message type
207    format!("urn:iso:std:iso:20022:tech:xsd:{}", message_type)
208}
209
210/// Convert message type to short form (e.g., "pacs.008.001.08" -> "pacs.008")
211pub fn normalize_message_type(message_type: &str) -> String {
212    for info in MESSAGE_REGISTRY {
213        if message_type == info.short_form || message_type == info.full_form {
214            return info.short_form.to_string();
215        }
216    }
217    message_type.to_string()
218}
219
220/// Map XML element name to message type short form
221pub fn element_to_message_type(element_name: &str) -> Option<&'static str> {
222    MESSAGE_REGISTRY
223        .iter()
224        .find(|info| info.xml_element_name == element_name || info.rust_type_name == element_name)
225        .map(|info| info.short_form)
226}
227
228/// Map message type to XML element name
229pub fn message_type_to_element(message_type: &str) -> Option<&'static str> {
230    MESSAGE_REGISTRY
231        .iter()
232        .find(|info| info.short_form == message_type || info.full_form == message_type)
233        .map(|info| info.xml_element_name)
234}
235
236/// Map message type to Rust type name
237pub fn message_type_to_rust_type(message_type: &str) -> Option<&'static str> {
238    MESSAGE_REGISTRY
239        .iter()
240        .find(|info| info.short_form == message_type || info.full_form == message_type)
241        .map(|info| info.rust_type_name)
242}
243
244/// Get full form of message type (e.g., "pacs.008" -> "pacs.008.001.08")
245pub fn get_full_form(message_type: &str) -> Option<&'static str> {
246    MESSAGE_REGISTRY
247        .iter()
248        .find(|info| info.short_form == message_type || info.full_form == message_type)
249        .map(|info| info.full_form)
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[test]
257    fn test_element_to_message_type() {
258        assert_eq!(
259            element_to_message_type("FIToFICstmrCdtTrf"),
260            Some("pacs.008")
261        );
262        assert_eq!(element_to_message_type("BkToCstmrStmt"), Some("camt.053"));
263        assert_eq!(
264            element_to_message_type("CstmrCdtTrfInitn"),
265            Some("pain.001")
266        );
267        assert_eq!(element_to_message_type("UnknownElement"), None);
268    }
269
270    #[test]
271    fn test_message_type_to_element() {
272        assert_eq!(
273            message_type_to_element("pacs.008"),
274            Some("FIToFICstmrCdtTrf")
275        );
276        assert_eq!(
277            message_type_to_element("pacs.008.001.08"),
278            Some("FIToFICstmrCdtTrf")
279        );
280        assert_eq!(message_type_to_element("unknown"), None);
281    }
282
283    #[test]
284    fn test_normalize_message_type() {
285        assert_eq!(normalize_message_type("pacs.008.001.08"), "pacs.008");
286        assert_eq!(normalize_message_type("pacs.008"), "pacs.008");
287        assert_eq!(normalize_message_type("unknown.type"), "unknown.type");
288    }
289
290    #[test]
291    fn test_get_namespace() {
292        assert_eq!(
293            get_namespace("pacs.008"),
294            "urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08"
295        );
296        assert_eq!(
297            get_namespace("camt.053"),
298            "urn:iso:std:iso:20022:tech:xsd:camt.053.001.08"
299        );
300    }
301
302    #[test]
303    fn test_get_full_form() {
304        assert_eq!(get_full_form("pacs.008"), Some("pacs.008.001.08"));
305        assert_eq!(get_full_form("pacs.008.001.08"), Some("pacs.008.001.08"));
306        assert_eq!(get_full_form("unknown"), None);
307    }
308}