mx_message/header/
bah_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// BranchAndFinancialInstitutionIdentification61: 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 BranchAndFinancialInstitutionIdentification61 {
27    #[serde(rename = "FinInstnId")]
28    pub fin_instn_id: FinancialInstitutionIdentification181,
29}
30
31impl BranchAndFinancialInstitutionIdentification61 {
32    pub fn validate(&self) -> Result<(), ValidationError> {
33        self.fin_instn_id.validate()?;
34        Ok(())
35    }
36}
37
38// BusinessApplicationHeader51: Relative indication of the processing precedence of the message over a (set of) Business Messages with assigned priorities.
39#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
40pub struct BusinessApplicationHeader51 {
41    #[serde(rename = "CharSet", skip_serializing_if = "Option::is_none")]
42    pub char_set: Option<String>,
43    #[serde(rename = "Fr")]
44    pub fr: Party44Choice1,
45    #[serde(rename = "To")]
46    pub to: Party44Choice1,
47    #[serde(rename = "BizMsgIdr")]
48    pub biz_msg_idr: String,
49    #[serde(rename = "MsgDefIdr")]
50    pub msg_def_idr: String,
51    #[serde(rename = "BizSvc", skip_serializing_if = "Option::is_none")]
52    pub biz_svc: Option<String>,
53    #[serde(rename = "CreDt")]
54    pub cre_dt: String,
55    #[serde(rename = "CpyDplct", skip_serializing_if = "Option::is_none")]
56    pub cpy_dplct: Option<CopyDuplicate1Code>,
57    #[serde(rename = "Prty", skip_serializing_if = "Option::is_none")]
58    pub prty: Option<String>,
59}
60
61impl BusinessApplicationHeader51 {
62    pub fn validate(&self) -> Result<(), ValidationError> {
63        self.fr.validate()?;
64        self.to.validate()?;
65        if self.biz_msg_idr.chars().count() < 1 {
66            return Err(ValidationError::new(
67                1001,
68                "biz_msg_idr is shorter than the minimum length of 1".to_string(),
69            ));
70        }
71        if self.biz_msg_idr.chars().count() > 35 {
72            return Err(ValidationError::new(
73                1002,
74                "biz_msg_idr exceeds the maximum length of 35".to_string(),
75            ));
76        }
77        if self.msg_def_idr.chars().count() < 1 {
78            return Err(ValidationError::new(
79                1001,
80                "msg_def_idr is shorter than the minimum length of 1".to_string(),
81            ));
82        }
83        if self.msg_def_idr.chars().count() > 35 {
84            return Err(ValidationError::new(
85                1002,
86                "msg_def_idr exceeds the maximum length of 35".to_string(),
87            ));
88        }
89        if let Some(ref val) = self.biz_svc {
90            if val.chars().count() < 1 {
91                return Err(ValidationError::new(
92                    1001,
93                    "biz_svc is shorter than the minimum length of 1".to_string(),
94                ));
95            }
96            if val.chars().count() > 35 {
97                return Err(ValidationError::new(
98                    1002,
99                    "biz_svc exceeds the maximum length of 35".to_string(),
100                ));
101            }
102        }
103        let pattern = Regex::new(".*(\\+|-)((0[0-9])|(1[0-4])):[0-5][0-9]").unwrap();
104        if !pattern.is_match(&self.cre_dt) {
105            return Err(ValidationError::new(
106                1005,
107                "cre_dt does not match the required pattern".to_string(),
108            ));
109        }
110        if let Some(ref val) = self.cpy_dplct {
111            val.validate()?
112        }
113        Ok(())
114    }
115}
116
117// BusinessApplicationHeaderV02: Specifies the Business Application Header(s) of the Business Message(s) to which this Business Message relates.
118// Can be used when replying to a query; can also be used when canceling or amending.
119#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
120pub struct BusinessApplicationHeaderV02 {
121    #[serde(rename = "CharSet", skip_serializing_if = "Option::is_none")]
122    pub char_set: Option<String>,
123    #[serde(rename = "Fr")]
124    pub fr: Party44Choice1,
125    #[serde(rename = "To")]
126    pub to: Party44Choice1,
127    #[serde(rename = "BizMsgIdr")]
128    pub biz_msg_idr: String,
129    #[serde(rename = "MsgDefIdr")]
130    pub msg_def_idr: String,
131    #[serde(rename = "BizSvc")]
132    pub biz_svc: Max35Textfixed,
133    #[serde(rename = "MktPrctc", skip_serializing_if = "Option::is_none")]
134    pub mkt_prctc: Option<ImplementationSpecification1>,
135    #[serde(rename = "CreDt")]
136    pub cre_dt: String,
137    #[serde(rename = "CpyDplct", skip_serializing_if = "Option::is_none")]
138    pub cpy_dplct: Option<CopyDuplicate1Code>,
139    #[serde(rename = "PssblDplct", skip_serializing_if = "Option::is_none")]
140    pub pssbl_dplct: Option<bool>,
141    #[serde(rename = "Prty", skip_serializing_if = "Option::is_none")]
142    pub prty: Option<Priority2Code>,
143    #[serde(rename = "Rltd", skip_serializing_if = "Option::is_none")]
144    pub rltd: Option<BusinessApplicationHeader51>,
145}
146
147impl BusinessApplicationHeaderV02 {
148    pub fn validate(&self) -> Result<(), ValidationError> {
149        self.fr.validate()?;
150        self.to.validate()?;
151        if self.biz_msg_idr.chars().count() < 1 {
152            return Err(ValidationError::new(
153                1001,
154                "biz_msg_idr is shorter than the minimum length of 1".to_string(),
155            ));
156        }
157        if self.biz_msg_idr.chars().count() > 35 {
158            return Err(ValidationError::new(
159                1002,
160                "biz_msg_idr exceeds the maximum length of 35".to_string(),
161            ));
162        }
163        if self.msg_def_idr.chars().count() < 1 {
164            return Err(ValidationError::new(
165                1001,
166                "msg_def_idr is shorter than the minimum length of 1".to_string(),
167            ));
168        }
169        if self.msg_def_idr.chars().count() > 35 {
170            return Err(ValidationError::new(
171                1002,
172                "msg_def_idr exceeds the maximum length of 35".to_string(),
173            ));
174        }
175        self.biz_svc.validate()?;
176        if let Some(ref val) = self.mkt_prctc {
177            val.validate()?
178        }
179        let pattern = Regex::new(".*(\\+|-)((0[0-9])|(1[0-4])):[0-5][0-9]").unwrap();
180        if !pattern.is_match(&self.cre_dt) {
181            return Err(ValidationError::new(
182                1005,
183                "cre_dt does not match the required pattern".to_string(),
184            ));
185        }
186        if let Some(ref val) = self.cpy_dplct {
187            val.validate()?
188        }
189        if let Some(ref val) = self.prty {
190            val.validate()?
191        }
192        if let Some(ref val) = self.rltd {
193            val.validate()?
194        }
195        Ok(())
196    }
197}
198
199// ClearingSystemIdentification2Choice1: Identification of a clearing system, in a coded form as published in an external list.
200#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
201pub struct ClearingSystemIdentification2Choice1 {
202    #[serde(rename = "Cd", skip_serializing_if = "Option::is_none")]
203    pub cd: Option<String>,
204}
205
206impl ClearingSystemIdentification2Choice1 {
207    pub fn validate(&self) -> Result<(), ValidationError> {
208        if let Some(ref val) = self.cd {
209            if val.chars().count() < 1 {
210                return Err(ValidationError::new(
211                    1001,
212                    "cd is shorter than the minimum length of 1".to_string(),
213                ));
214            }
215            if val.chars().count() > 5 {
216                return Err(ValidationError::new(
217                    1002,
218                    "cd exceeds the maximum length of 5".to_string(),
219                ));
220            }
221        }
222        Ok(())
223    }
224}
225
226// ClearingSystemMemberIdentification21: Identification of a member of a clearing system.
227#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
228pub struct ClearingSystemMemberIdentification21 {
229    #[serde(rename = "ClrSysId")]
230    pub clr_sys_id: ClearingSystemIdentification2Choice1,
231    #[serde(rename = "MmbId")]
232    pub mmb_id: String,
233}
234
235impl ClearingSystemMemberIdentification21 {
236    pub fn validate(&self) -> Result<(), ValidationError> {
237        self.clr_sys_id.validate()?;
238        if self.mmb_id.chars().count() < 1 {
239            return Err(ValidationError::new(
240                1001,
241                "mmb_id is shorter than the minimum length of 1".to_string(),
242            ));
243        }
244        if self.mmb_id.chars().count() > 28 {
245            return Err(ValidationError::new(
246                1002,
247                "mmb_id exceeds the maximum length of 28".to_string(),
248            ));
249        }
250        let pattern = Regex::new("[0-9a-zA-Z/\\-\\?:\\(\\)\\.,'\\+ ]+").unwrap();
251        if !pattern.is_match(&self.mmb_id) {
252            return Err(ValidationError::new(
253                1005,
254                "mmb_id does not match the required pattern".to_string(),
255            ));
256        }
257        Ok(())
258    }
259}
260
261// CopyDuplicate1Code: Message is for information/confirmation purposes. It is a duplicate of a message previously sent.
262#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
263pub enum CopyDuplicate1Code {
264    #[default]
265    #[serde(rename = "CODU")]
266    CodeCODU,
267    #[serde(rename = "COPY")]
268    CodeCOPY,
269    #[serde(rename = "DUPL")]
270    CodeDUPL,
271}
272
273impl CopyDuplicate1Code {
274    pub fn validate(&self) -> Result<(), ValidationError> {
275        Ok(())
276    }
277}
278
279// FinancialInstitutionIdentification181: Legal entity identifier of the financial institution.
280#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
281pub struct FinancialInstitutionIdentification181 {
282    #[serde(rename = "BICFI")]
283    pub bicfi: String,
284    #[serde(rename = "ClrSysMmbId", skip_serializing_if = "Option::is_none")]
285    pub clr_sys_mmb_id: Option<ClearingSystemMemberIdentification21>,
286    #[serde(rename = "LEI", skip_serializing_if = "Option::is_none")]
287    pub lei: Option<String>,
288}
289
290impl FinancialInstitutionIdentification181 {
291    pub fn validate(&self) -> Result<(), ValidationError> {
292        let pattern =
293            Regex::new("[A-Z0-9]{4,4}[A-Z]{2,2}[A-Z0-9]{2,2}([A-Z0-9]{3,3}){0,1}").unwrap();
294        if !pattern.is_match(&self.bicfi) {
295            return Err(ValidationError::new(
296                1005,
297                "bicfi does not match the required pattern".to_string(),
298            ));
299        }
300        if let Some(ref val) = self.clr_sys_mmb_id {
301            val.validate()?
302        }
303        if let Some(ref val) = self.lei {
304            let pattern = Regex::new("[A-Z0-9]{18,18}[0-9]{2,2}").unwrap();
305            if !pattern.is_match(val) {
306                return Err(ValidationError::new(
307                    1005,
308                    "lei does not match the required pattern".to_string(),
309                ));
310            }
311        }
312        Ok(())
313    }
314}
315
316// ImplementationSpecification1: Identifier which unambiguously identifies, within the implementation specification registry, the implementation specification to which the ISO 20022 message is compliant. This can be done via a URN. It can also contain a version number or date.
317// For instance, "2018-01-01 – Version 2" or "urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66".
318#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
319pub struct ImplementationSpecification1 {
320    #[serde(rename = "Regy")]
321    pub regy: String,
322    #[serde(rename = "Id")]
323    pub id: String,
324}
325
326impl ImplementationSpecification1 {
327    pub fn validate(&self) -> Result<(), ValidationError> {
328        if self.regy.chars().count() < 1 {
329            return Err(ValidationError::new(
330                1001,
331                "regy is shorter than the minimum length of 1".to_string(),
332            ));
333        }
334        if self.regy.chars().count() > 350 {
335            return Err(ValidationError::new(
336                1002,
337                "regy exceeds the maximum length of 350".to_string(),
338            ));
339        }
340        if self.id.chars().count() < 1 {
341            return Err(ValidationError::new(
342                1001,
343                "id is shorter than the minimum length of 1".to_string(),
344            ));
345        }
346        if self.id.chars().count() > 2048 {
347            return Err(ValidationError::new(
348                1002,
349                "id exceeds the maximum length of 2048".to_string(),
350            ));
351        }
352        Ok(())
353    }
354}
355
356// Max35Text_fixed: swift.cbprplus.01
357#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
358pub enum Max35Textfixed {
359    #[default]
360    #[serde(rename = "swift.cbprplus.01")]
361    CodeSWIFTCBPRPLUS01,
362}
363
364impl Max35Textfixed {
365    pub fn validate(&self) -> Result<(), ValidationError> {
366        Ok(())
367    }
368}
369
370// Party44Choice1: Identification of a financial institution.
371#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
372pub struct Party44Choice1 {
373    #[serde(rename = "FIId", skip_serializing_if = "Option::is_none")]
374    pub fi_id: Option<BranchAndFinancialInstitutionIdentification61>,
375}
376
377impl Party44Choice1 {
378    pub fn validate(&self) -> Result<(), ValidationError> {
379        if let Some(ref val) = self.fi_id {
380            val.validate()?
381        }
382        Ok(())
383    }
384}
385
386// Priority2Code: Priority level is normal.
387#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
388pub enum Priority2Code {
389    #[default]
390    #[serde(rename = "HIGH")]
391    CodeHIGH,
392    #[serde(rename = "NORM")]
393    CodeNORM,
394}
395
396impl Priority2Code {
397    pub fn validate(&self) -> Result<(), ValidationError> {
398        Ok(())
399    }
400}