mx_message/document/
admi_024_001_01.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
20use crate::error::*;
21use regex::Regex;
22use serde::{Deserialize, Serialize};
23
24// BranchAndFinancialInstitutionIdentification81: Unique and unambiguous identification of a financial institution, as assigned under an internationally recognised or proprietary identification scheme.
25#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
26pub struct BranchAndFinancialInstitutionIdentification81 {
27    #[serde(rename = "FinInstnId")]
28    pub fin_instn_id: FinancialInstitutionIdentification231,
29}
30
31impl BranchAndFinancialInstitutionIdentification81 {
32    pub fn validate(&self) -> Result<(), ValidationError> {
33        self.fin_instn_id.validate()?;
34        Ok(())
35    }
36}
37
38// CorrespondenceNotification11: Provides information about the notification in narrative form.
39#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
40pub struct CorrespondenceNotification11 {
41    #[serde(rename = "SndrNtfctnId")]
42    pub sndr_ntfctn_id: String,
43    #[serde(rename = "NtfctnTp")]
44    pub ntfctn_tp: NotificationType1Choice1,
45    #[serde(rename = "NtfctnNrrtv")]
46    pub ntfctn_nrrtv: Vec<String>,
47}
48
49impl CorrespondenceNotification11 {
50    pub fn validate(&self) -> Result<(), ValidationError> {
51        if self.sndr_ntfctn_id.chars().count() < 1 {
52            return Err(ValidationError::new(
53                1001,
54                "sndr_ntfctn_id is shorter than the minimum length of 1".to_string(),
55            ));
56        }
57        if self.sndr_ntfctn_id.chars().count() > 35 {
58            return Err(ValidationError::new(
59                1002,
60                "sndr_ntfctn_id exceeds the maximum length of 35".to_string(),
61            ));
62        }
63        let pattern = Regex::new("[0-9a-zA-Z/\\-\\?:\\(\\)\\.,'\\+ ]+").unwrap();
64        if !pattern.is_match(&self.sndr_ntfctn_id) {
65            return Err(ValidationError::new(
66                1005,
67                "sndr_ntfctn_id does not match the required pattern".to_string(),
68            ));
69        }
70        self.ntfctn_tp.validate()?;
71        for item in &self.ntfctn_nrrtv {
72            if item.chars().count() < 1 {
73                return Err(ValidationError::new(
74                    1001,
75                    "ntfctn_nrrtv is shorter than the minimum length of 1".to_string(),
76                ));
77            }
78            if item.chars().count() > 2000 {
79                return Err(ValidationError::new(
80                    1002,
81                    "ntfctn_nrrtv exceeds the maximum length of 2000".to_string(),
82                ));
83            }
84            let pattern = Regex::new(
85                "[0-9a-zA-Z/\\-\\?:\\(\\)\\.,'\\+ !#$%&\\*=^_`\\{\\|\\}~\";<>@\\[\\\\\\]]+",
86            )
87            .unwrap();
88            if !pattern.is_match(&item) {
89                return Err(ValidationError::new(
90                    1005,
91                    "ntfctn_nrrtv does not match the required pattern".to_string(),
92                ));
93            }
94        }
95        Ok(())
96    }
97}
98
99// FinancialInstitutionIdentification231: Code allocated to a financial institution by the ISO 9362 Registration Authority as described in ISO 9362 "Banking - Banking telecommunication messages - Business identifier code (BIC)".
100#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
101pub struct FinancialInstitutionIdentification231 {
102    #[serde(rename = "BICFI")]
103    pub bicfi: String,
104}
105
106impl FinancialInstitutionIdentification231 {
107    pub fn validate(&self) -> Result<(), ValidationError> {
108        let pattern =
109            Regex::new("[A-Z0-9]{4,4}[A-Z]{2,2}[A-Z0-9]{2,2}([A-Z0-9]{3,3}){0,1}").unwrap();
110        if !pattern.is_match(&self.bicfi) {
111            return Err(ValidationError::new(
112                1005,
113                "bicfi does not match the required pattern".to_string(),
114            ));
115        }
116        Ok(())
117    }
118}
119
120// GroupHeader1291: Party that receives the notification.
121#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
122pub struct GroupHeader1291 {
123    #[serde(rename = "MsgId")]
124    pub msg_id: String,
125    #[serde(rename = "CreDtTm")]
126    pub cre_dt_tm: String,
127    #[serde(rename = "Sndr")]
128    pub sndr: Party50Choice1,
129    #[serde(rename = "Rcvr")]
130    pub rcvr: Party50Choice1,
131}
132
133impl GroupHeader1291 {
134    pub fn validate(&self) -> Result<(), ValidationError> {
135        if self.msg_id.chars().count() < 1 {
136            return Err(ValidationError::new(
137                1001,
138                "msg_id is shorter than the minimum length of 1".to_string(),
139            ));
140        }
141        if self.msg_id.chars().count() > 35 {
142            return Err(ValidationError::new(
143                1002,
144                "msg_id exceeds the maximum length of 35".to_string(),
145            ));
146        }
147        let pattern = Regex::new("[0-9a-zA-Z/\\-\\?:\\(\\)\\.,'\\+ ]+").unwrap();
148        if !pattern.is_match(&self.msg_id) {
149            return Err(ValidationError::new(
150                1005,
151                "msg_id does not match the required pattern".to_string(),
152            ));
153        }
154        let pattern = Regex::new(".*(\\+|-)((0[0-9])|(1[0-4])):[0-5][0-9]").unwrap();
155        if !pattern.is_match(&self.cre_dt_tm) {
156            return Err(ValidationError::new(
157                1005,
158                "cre_dt_tm does not match the required pattern".to_string(),
159            ));
160        }
161        self.sndr.validate()?;
162        self.rcvr.validate()?;
163        Ok(())
164    }
165}
166
167// NotificationOfCorrespondenceV01: Set of elements used to provide further details on the notification.
168#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
169pub struct NotificationOfCorrespondenceV01 {
170    #[serde(rename = "GrpHdr")]
171    pub grp_hdr: GroupHeader1291,
172    #[serde(rename = "NtfctnData")]
173    pub ntfctn_data: CorrespondenceNotification11,
174}
175
176impl NotificationOfCorrespondenceV01 {
177    pub fn validate(&self) -> Result<(), ValidationError> {
178        self.grp_hdr.validate()?;
179        self.ntfctn_data.validate()?;
180        Ok(())
181    }
182}
183
184// NotificationType1Choice1: Notification type, as published in an external notification type code set.
185#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
186pub struct NotificationType1Choice1 {
187    #[serde(rename = "Cd", skip_serializing_if = "Option::is_none")]
188    pub cd: Option<String>,
189}
190
191impl NotificationType1Choice1 {
192    pub fn validate(&self) -> Result<(), ValidationError> {
193        if let Some(ref val) = self.cd {
194            if val.chars().count() < 1 {
195                return Err(ValidationError::new(
196                    1001,
197                    "cd is shorter than the minimum length of 1".to_string(),
198                ));
199            }
200            if val.chars().count() > 4 {
201                return Err(ValidationError::new(
202                    1002,
203                    "cd exceeds the maximum length of 4".to_string(),
204                ));
205            }
206        }
207        Ok(())
208    }
209}
210
211// Party50Choice1: Identification of a financial institution.
212#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
213pub struct Party50Choice1 {
214    #[serde(rename = "Agt", skip_serializing_if = "Option::is_none")]
215    pub agt: Option<BranchAndFinancialInstitutionIdentification81>,
216}
217
218impl Party50Choice1 {
219    pub fn validate(&self) -> Result<(), ValidationError> {
220        if let Some(ref val) = self.agt {
221            val.validate()?
222        }
223        Ok(())
224    }
225}