1pub use country_codes::{country, CountryCode};
14pub use types::{one_to_n::OneToN, zero_to_n::ZeroToN};
15
16mod country_codes;
17mod types;
18
19use lei::registration_authority::RegistrationAuthority;
20
21#[derive(serde::Serialize, serde::Deserialize)]
23#[serde(rename_all = "camelCase")]
24#[serde(deny_unknown_fields)]
25pub struct IVMS101 {
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub originator: Option<Originator>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub beneficiary: Option<Beneficiary>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 #[serde(rename = "originatingVASP")]
35 pub originating_vasp: Option<OriginatingVASP>,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 #[serde(rename = "beneficiaryVASP")]
39 pub beneficiary_vasp: Option<BeneficiaryVASP>,
40}
41
42impl Validatable for IVMS101 {
43 fn validate(&self) -> Result<(), Error> {
44 if let Some(o) = &self.originator {
45 o.validate()?;
46 }
47 if let Some(b) = &self.beneficiary {
48 b.validate()?;
49 }
50 if let Some(ov) = &self.originating_vasp {
51 ov.validate()?;
52 }
53 if let Some(bv) = &self.beneficiary_vasp {
54 bv.validate()?;
55 }
56 Ok(())
57 }
58}
59
60#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
62#[serde(rename_all = "camelCase")]
63#[serde(deny_unknown_fields)]
64pub struct Originator {
65 pub originator_persons: OneToN<Person>,
67 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
69 pub account_number: ZeroToN<types::StringMax100>,
70}
71
72impl Validatable for Originator {
73 fn validate(&self) -> Result<(), Error> {
74 for person in self.originator_persons.clone() {
75 if let Person::NaturalPerson(np) = &person {
76 if np.geographic_address.is_empty()
77 && np.customer_identification.is_none()
78 && np.national_identification.is_none()
79 && np.date_and_place_of_birth.is_none()
80 {
81 return Err(
82 "Natural person: one of 1) geographic address 2) customer id 3) national id 4) date and place of birth is required (IVMS101 C1)".into());
83 }
84 };
85 person.validate()?;
86 }
87 Ok(())
88 }
89}
90
91impl Originator {
92 pub fn new(person: Person) -> Result<Self, Error> {
98 Ok(Self {
99 originator_persons: person.into(),
100 account_number: None.into(),
101 })
102 }
103}
104
105#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
107#[serde(rename_all = "camelCase")]
108#[serde(deny_unknown_fields)]
109pub struct Beneficiary {
110 pub beneficiary_persons: OneToN<Person>,
112 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
114 pub account_number: ZeroToN<types::StringMax100>,
115}
116
117impl Validatable for Beneficiary {
118 fn validate(&self) -> Result<(), Error> {
119 for person in self.beneficiary_persons.clone() {
120 person.validate()?;
121 }
122 Ok(())
123 }
124}
125
126impl Beneficiary {
127 pub fn new(person: Person, account_number: Option<&str>) -> Result<Self, Error> {
133 Ok(Self {
134 beneficiary_persons: person.into(),
135 account_number: account_number.map(TryInto::try_into).transpose()?.into(),
136 })
137 }
138}
139
140#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
142#[serde(deny_unknown_fields)]
143pub struct OriginatingVASP {
144 #[serde(rename = "originatingVASP")]
146 pub originating_vasp: Person,
147}
148
149impl OriginatingVASP {
150 pub fn new(name: &str, lei: &lei::LEI) -> Result<Self, Error> {
156 Ok(Self {
157 originating_vasp: Person::LegalPerson(LegalPerson {
158 name: LegalPersonName {
159 name_identifier: LegalPersonNameID {
160 legal_person_name: name.try_into()?,
161 legal_person_name_identifier_type: LegalPersonNameTypeCode::Legal,
162 }
163 .into(),
164 local_name_identifier: None.into(),
165 phonetic_name_identifier: None.into(),
166 },
167 geographic_address: ZeroToN::None,
168 customer_identification: None,
169 national_identification: Some(NationalIdentification {
170 national_identifier: lei.to_string().as_str().try_into().unwrap(),
171 national_identifier_type: NationalIdentifierTypeCode::LegalEntityIdentifier,
172 country_of_issue: None,
173 registration_authority: None,
174 }),
175 country_of_registration: None,
176 }),
177 })
178 }
179
180 pub fn lei(&self) -> Result<Option<lei::LEI>, lei::Error> {
187 self.originating_vasp.lei()
188 }
189}
190
191impl Validatable for OriginatingVASP {
192 fn validate(&self) -> Result<(), Error> {
193 self.originating_vasp.validate()
194 }
195}
196
197#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
199#[serde(deny_unknown_fields)]
200pub struct BeneficiaryVASP {
201 #[serde(skip_serializing_if = "Option::is_none")]
203 #[serde(rename = "beneficiaryVASP")]
204 pub beneficiary_vasp: Option<Person>,
205}
206
207impl Validatable for BeneficiaryVASP {
208 fn validate(&self) -> Result<(), Error> {
209 match &self.beneficiary_vasp {
210 None => Ok(()),
211 Some(p) => p.validate(),
212 }
213 }
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
218#[serde(rename_all = "camelCase")]
219#[serde(deny_unknown_fields)]
220pub enum Person {
221 NaturalPerson(NaturalPerson),
222 LegalPerson(LegalPerson),
223}
224
225impl Person {
226 #[must_use]
228 pub fn first_name(&self) -> Option<String> {
229 match self {
230 Self::NaturalPerson(p) => p.first_name(),
231 Self::LegalPerson(_p) => None,
232 }
233 }
234
235 #[must_use]
237 pub fn last_name(&self) -> String {
238 match self {
239 Self::NaturalPerson(p) => p.last_name(),
240 Self::LegalPerson(p) => p.name(),
241 }
242 }
243
244 #[must_use]
246 pub fn address(&self) -> Option<&Address> {
247 match self {
248 Self::NaturalPerson(p) => p.address(),
249 Self::LegalPerson(p) => p.address(),
250 }
251 }
252
253 #[must_use]
255 pub fn customer_identification(&self) -> Option<String> {
256 match self {
257 Self::NaturalPerson(p) => p.customer_identification.clone().map(|s| s.to_string()),
258 Self::LegalPerson(p) => p.customer_identification.clone().map(|s| s.to_string()),
259 }
260 }
261
262 pub fn lei(&self) -> Result<Option<lei::LEI>, lei::Error> {
265 match self {
266 Self::NaturalPerson(_) => Ok(None),
267 Self::LegalPerson(l) => l.lei(),
268 }
269 }
270}
271
272impl Validatable for Person {
273 fn validate(&self) -> Result<(), Error> {
274 match self {
275 Person::NaturalPerson(p) => p.validate(),
276 Person::LegalPerson(p) => p.validate(),
277 }
278 }
279}
280
281#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
283#[serde(rename_all = "camelCase")]
284#[serde(deny_unknown_fields)]
285pub struct NaturalPerson {
286 pub name: OneToN<NaturalPersonName>,
288 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
290 pub geographic_address: ZeroToN<Address>,
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub national_identification: Option<NationalIdentification>,
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub customer_identification: Option<types::StringMax50>,
297 #[serde(skip_serializing_if = "Option::is_none")]
299 pub date_and_place_of_birth: Option<DateAndPlaceOfBirth>,
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub country_of_residence: Option<CountryCode>,
303}
304
305impl NaturalPerson {
306 pub fn new(
313 first_name: &str,
314 last_name: &str,
315 customer_identification: Option<&str>,
316 address: Option<Address>,
317 ) -> Result<Self, Error> {
318 Ok(Self {
319 name: NaturalPersonName {
320 name_identifier: NaturalPersonNameID {
321 primary_identifier: last_name.try_into()?,
322 secondary_identifier: Some(first_name.try_into()?),
323 name_identifier_type: NaturalPersonNameTypeCode::LegalName,
324 }
325 .into(),
326 local_name_identifier: None.into(),
327 phonetic_name_identifier: None.into(),
328 }
329 .into(),
330 geographic_address: address.into(),
331 national_identification: None,
332 customer_identification: customer_identification.map(TryInto::try_into).transpose()?,
333 date_and_place_of_birth: None,
334 country_of_residence: None,
335 })
336 }
337
338 #[must_use]
339 fn first_name(&self) -> Option<String> {
340 Some(
341 self.name
342 .first()
343 .name_identifier
344 .first()
345 .clone()
346 .secondary_identifier?
347 .into(),
348 )
349 }
350
351 #[must_use]
352 fn last_name(&self) -> String {
353 self.name
354 .first()
355 .name_identifier
356 .first()
357 .primary_identifier
358 .to_string()
359 }
360
361 #[must_use]
362 fn address(&self) -> Option<&Address> {
363 self.geographic_address.first()
364 }
365}
366
367impl Validatable for NaturalPerson {
368 fn validate(&self) -> Result<(), Error> {
369 self.name
370 .clone()
371 .into_iter()
372 .try_for_each(|name| name.validate())?;
373 self.geographic_address
374 .clone()
375 .into_iter()
376 .try_for_each(|addr| addr.validate())?;
377
378 Ok(())
379 }
380}
381
382#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
384#[serde(rename_all = "camelCase")]
385#[serde(deny_unknown_fields)]
386pub struct NaturalPersonName {
387 pub name_identifier: OneToN<NaturalPersonNameID>,
389 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
390 pub local_name_identifier: ZeroToN<NaturalPersonNameID>,
391 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
392 pub phonetic_name_identifier: ZeroToN<NaturalPersonNameID>,
393}
394
395impl Validatable for NaturalPersonName {
396 fn validate(&self) -> Result<(), Error> {
397 let has_legl = self
398 .name_identifier
399 .clone()
400 .into_iter()
401 .any(|ni| ni.name_identifier_type == NaturalPersonNameTypeCode::LegalName);
402 if !has_legl {
403 return Err("Natural person must have a legal name id (IVMS101 C6)".into());
404 }
405 Ok(())
406 }
407}
408
409#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
411#[serde(rename_all = "camelCase")]
412#[serde(deny_unknown_fields)]
413pub struct NaturalPersonNameID {
414 pub primary_identifier: types::StringMax100,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub secondary_identifier: Option<types::StringMax100>,
419 pub name_identifier_type: NaturalPersonNameTypeCode,
421}
422
423#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
425#[serde(rename_all = "camelCase")]
426#[serde(deny_unknown_fields)]
427pub struct Address {
428 pub address_type: AddressTypeCode,
430 #[serde(skip_serializing_if = "Option::is_none")]
432 pub department: Option<types::StringMax50>,
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub sub_department: Option<types::StringMax70>,
436 #[serde(skip_serializing_if = "Option::is_none")]
438 pub street_name: Option<types::StringMax70>,
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub building_number: Option<types::StringMax16>,
442 #[serde(skip_serializing_if = "Option::is_none")]
444 pub building_name: Option<types::StringMax35>,
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub floor: Option<types::StringMax70>,
448 #[serde(skip_serializing_if = "Option::is_none")]
450 pub post_box: Option<types::StringMax16>,
451 #[serde(skip_serializing_if = "Option::is_none")]
453 pub room: Option<types::StringMax70>,
454 #[serde(skip_serializing_if = "Option::is_none")]
456 pub post_code: Option<types::StringMax16>,
457 pub town_name: types::StringMax35,
459 #[serde(skip_serializing_if = "Option::is_none")]
461 pub town_location_name: Option<types::StringMax35>,
462 #[serde(skip_serializing_if = "Option::is_none")]
464 pub district_name: Option<types::StringMax35>,
465 #[serde(skip_serializing_if = "Option::is_none")]
467 pub country_sub_division: Option<types::StringMax35>,
468 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
470 pub address_line: ZeroToN<types::StringMax70>,
471 pub country: CountryCode,
473}
474
475impl Address {
476 pub fn new(
482 street: Option<&str>,
483 number: Option<&str>,
484 address_line: Option<&str>,
485 postal_code: &str,
486 town: &str,
487 country: &str,
488 ) -> Result<Self, Error> {
489 Ok(Self {
490 address_type: AddressTypeCode::Residential,
491 department: None,
492 sub_department: None,
493 street_name: street.map(TryInto::try_into).transpose()?,
494 building_number: number.map(TryInto::try_into).transpose()?,
495 building_name: None,
496 floor: None,
497 post_box: None,
498 room: None,
499 post_code: Some(postal_code.try_into()?),
500 town_name: town.try_into()?,
501 town_location_name: None,
502 district_name: None,
503 country_sub_division: None,
504 address_line: address_line.map(TryInto::try_into).transpose()?.into(),
505 country: country.try_into()?,
506 })
507 }
508
509 #[must_use]
512 pub fn address_lines(&self) -> Option<String> {
513 if self.address_line.is_empty() {
514 None
515 } else {
516 Some(
517 self.address_line
518 .clone()
519 .into_iter()
520 .map(Into::into)
521 .collect::<Vec<String>>()
522 .join(", "),
523 )
524 }
525 }
526}
527
528impl std::fmt::Display for Address {
529 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
530 format_address(
531 f,
532 self.street_name.as_ref().map(types::StringMax70::as_str),
533 self.building_number
534 .as_ref()
535 .map(types::StringMax16::as_str),
536 self.address_lines().as_deref(),
537 self.post_code.as_ref().map(types::StringMax16::as_str),
538 self.town_name.as_str(),
539 self.country.as_str(),
540 )
541 }
542}
543
544pub fn format_address(
549 f: &mut std::fmt::Formatter,
550 street: Option<&str>,
551 number: Option<&str>,
552 address_line: Option<&str>,
553 postcode: Option<&str>,
554 town: &str,
555 country_code: &str,
556) -> std::fmt::Result {
557 if let Some(s) = street {
558 write!(f, "{s}")?;
559 if let Some(n) = number {
560 write!(f, " {n}")?;
561 }
562 write!(f, ", ")?;
563 }
564 if let Some(al) = address_line {
565 write!(f, "{al}, ")?;
566 }
567 if let Some(pc) = postcode {
568 write!(f, "{pc} ")?;
569 }
570 write!(
571 f,
572 "{town}, {}",
573 country(country_code.to_lowercase().as_str()).unwrap_or(country_code)
574 )
575}
576
577impl Validatable for Address {
578 fn validate(&self) -> Result<(), Error> {
579 if self.address_line.is_empty()
580 && (self.street_name.is_none()
581 || (self.building_name.is_none() && self.building_number.is_none()))
582 {
583 return Err("Either 1) address line or 2) street name and either building name or building number are required (IVMS101 C8)".into());
584 }
585 Ok(())
586 }
587}
588
589#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
591#[serde(rename_all = "camelCase")]
592#[serde(deny_unknown_fields)]
593pub struct DateAndPlaceOfBirth {
594 pub date_of_birth: Date,
596 pub place_of_birth: types::StringMax70,
598}
599
600impl Validatable for DateAndPlaceOfBirth {
601 fn validate(&self) -> Result<(), Error> {
602 if self.date_of_birth >= chrono::prelude::Utc::now().date_naive() {
603 return Err("Date of birth must be in the past (IVMS101 C2)".into());
604 }
605 Ok(())
606 }
607}
608
609#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
611#[serde(rename_all = "camelCase")]
612#[serde(deny_unknown_fields)]
613pub struct NationalIdentification {
614 pub national_identifier: types::StringMax35,
616 pub national_identifier_type: NationalIdentifierTypeCode,
618 #[serde(skip_serializing_if = "Option::is_none")]
620 pub country_of_issue: Option<CountryCode>,
621 #[serde(skip_serializing_if = "Option::is_none")]
623 pub registration_authority: Option<RegistrationAuthority>,
624}
625
626#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
628#[serde(rename_all = "camelCase")]
629#[serde(deny_unknown_fields)]
630pub struct LegalPerson {
631 pub name: LegalPersonName,
633 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
635 pub geographic_address: ZeroToN<Address>,
636 #[serde(skip_serializing_if = "Option::is_none")]
638 pub customer_identification: Option<types::StringMax50>,
639 #[serde(skip_serializing_if = "Option::is_none")]
641 pub national_identification: Option<NationalIdentification>,
642 #[serde(skip_serializing_if = "Option::is_none")]
644 pub country_of_registration: Option<CountryCode>,
645}
646
647impl LegalPerson {
648 pub fn new(
655 name: &str,
656 customer_identification: &str,
657 address: Address,
658 lei: &lei::LEI,
659 ) -> Result<Self, Error> {
660 Ok(Self {
661 name: LegalPersonName {
662 name_identifier: LegalPersonNameID {
663 legal_person_name: name.try_into()?,
664 legal_person_name_identifier_type: LegalPersonNameTypeCode::Legal,
665 }
666 .into(),
667 local_name_identifier: None.into(),
668 phonetic_name_identifier: None.into(),
669 },
670 geographic_address: Some(address).into(),
671 customer_identification: Some(customer_identification.try_into()?),
672 national_identification: Some(NationalIdentification {
673 national_identifier: lei.to_string().as_str().try_into().unwrap(),
674 national_identifier_type: NationalIdentifierTypeCode::LegalEntityIdentifier,
675 country_of_issue: None,
676 registration_authority: None,
677 }),
678 country_of_registration: None,
679 })
680 }
681
682 fn lei(&self) -> Result<Option<lei::LEI>, lei::Error> {
683 self.national_identification
684 .as_ref()
685 .map(|ni| lei::LEI::try_from(ni.national_identifier.to_string().as_str()))
686 .transpose()
687 }
688}
689
690impl LegalPerson {
691 #[must_use]
692 fn name(&self) -> String {
693 self.name
694 .name_identifier
695 .first()
696 .legal_person_name
697 .to_string()
698 }
699
700 #[must_use]
701 fn address(&self) -> Option<&Address> {
702 self.geographic_address.first()
703 }
704}
705
706impl Validatable for LegalPerson {
707 fn validate(&self) -> Result<(), Error> {
708 let has_geog = self
709 .geographic_address
710 .clone()
711 .into_iter()
712 .any(|addr| addr.address_type == AddressTypeCode::Residential);
713 if !has_geog
714 && self.national_identification.is_none()
715 && self.customer_identification.is_none()
716 {
717 return Err(
718 "Legal person needs either geographic address, customer number or national identification (IVMS101 C4)"
719 .into(),
720 );
721 }
722 if let Some(ni) = &self.national_identification {
723 if !matches!(
724 ni.national_identifier_type,
725 NationalIdentifierTypeCode::RegistrationAuthorityIdentifier
726 | NationalIdentifierTypeCode::Unspecified
727 | NationalIdentifierTypeCode::LegalEntityIdentifier
728 | NationalIdentifierTypeCode::TaxIdentificationNumber
729 ) {
730 return Err("Legal person must have a 'RAID', 'MISC', 'LEIX' or 'TXID' identification (IVMS101 C7)".into());
731 }
732 };
733 if let Some(ni) = &self.national_identification {
734 if ni.national_identifier_type == NationalIdentifierTypeCode::LegalEntityIdentifier {
735 if let Err(e) = lei::LEI::try_from(ni.national_identifier.as_str()) {
736 return Err(format!("Invalid LEI: {e} (IVMS101 C11)").as_str().into());
737 }
738 }
739 };
740 self.name.validate()?;
741 self.geographic_address
742 .clone()
743 .into_iter()
744 .try_for_each(|addr| addr.validate())?;
745 match &self.national_identification {
746 Some(ni) => {
747 if ni.country_of_issue.is_some() {
748 return Err("Legal person must not have a country of issue (IVMS101 C9)".into());
749 }
750 if ni.national_identifier_type != NationalIdentifierTypeCode::LegalEntityIdentifier
751 && ni.registration_authority.is_none()
752 {
753 return Err("Legal person must specify registration authority for non-'LEIX' identification (IVMS101 C9)".into());
754 }
755 if ni.national_identifier_type == NationalIdentifierTypeCode::LegalEntityIdentifier
756 && ni.registration_authority.is_some()
757 {
758 return Err("Legal person must not specify registration authority for 'LEIX' identification (IVMS101 C9)".into());
759 }
760 }
761 None => (),
762 }
763 Ok(())
764 }
765}
766
767#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
769#[serde(rename_all = "camelCase")]
770#[serde(deny_unknown_fields)]
771pub struct LegalPersonName {
772 pub name_identifier: OneToN<LegalPersonNameID>,
774 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
776 pub local_name_identifier: ZeroToN<LegalPersonNameID>,
777 #[serde(default, skip_serializing_if = "ZeroToN::is_empty")]
779 pub phonetic_name_identifier: ZeroToN<LegalPersonNameID>,
780}
781
782impl Validatable for LegalPersonName {
783 fn validate(&self) -> Result<(), Error> {
784 let has_legl = self
785 .name_identifier
786 .clone()
787 .into_iter()
788 .any(|ni| ni.legal_person_name_identifier_type == LegalPersonNameTypeCode::Legal);
789 if !has_legl {
790 return Err("Legal person must have a legal name id (IVMS101 C5)".into());
791 }
792 Ok(())
793 }
794}
795
796#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
798#[serde(rename_all = "camelCase")]
799#[serde(deny_unknown_fields)]
800pub struct LegalPersonNameID {
801 pub legal_person_name: types::StringMax100,
803 pub legal_person_name_identifier_type: LegalPersonNameTypeCode,
805}
806
807#[derive(serde::Serialize, serde::Deserialize)]
809#[serde(rename_all = "camelCase")]
810#[serde(deny_unknown_fields)]
811pub struct IntermediaryVASP {
812 pub intermediary_vasp: Person,
814 pub sequence: u32,
816}
817
818impl Validatable for IntermediaryVASP {
820 fn validate(&self) -> Result<(), Error> {
821 self.intermediary_vasp.validate()?;
822 Ok(())
823 }
824}
825
826#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
828pub enum NaturalPersonNameTypeCode {
829 #[serde(rename = "ALIA")]
830 Alias,
831 #[serde(rename = "BIRT")]
832 NameAtBirth,
833 #[serde(rename = "MAID")]
834 MaidenName,
835 #[serde(rename = "LEGL")]
836 LegalName,
837 #[serde(rename = "MISC")]
838 Unspecified,
839}
840
841#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
843pub enum LegalPersonNameTypeCode {
844 #[serde(rename = "LEGL")]
845 Legal,
846 #[serde(rename = "SHRT")]
847 Short,
848 #[serde(rename = "TRAD")]
849 Trading,
850}
851
852type Date = chrono::NaiveDate;
853
854#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
856pub enum AddressTypeCode {
857 #[serde(rename = "HOME")]
858 Residential,
859 #[serde(rename = "BIZZ")]
860 Business,
861 #[serde(rename = "GEOG")]
862 Geographic,
863}
864
865#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
867pub enum NationalIdentifierTypeCode {
868 #[serde(rename = "ARNU")]
869 AlienRegistrationNumber,
870 #[serde(rename = "CCPT")]
871 PassportNumber,
872 #[serde(rename = "RAID")]
873 RegistrationAuthorityIdentifier,
874 #[serde(rename = "DRLC")]
875 DriverLicenseNumber,
876 #[serde(rename = "FIIN")]
877 ForeignInvestmentIdentityNumber,
878 #[serde(rename = "TXID")]
879 TaxIdentificationNumber,
880 #[serde(rename = "SOCS")]
881 SocialSecurityNumber,
882 #[serde(rename = "IDCD")]
883 IdentityCardNumber,
884 #[serde(rename = "LEIX")]
885 LegalEntityIdentifier,
886 #[serde(rename = "MISC")]
887 Unspecified,
888}
889
890pub trait Validatable {
893 fn validate(&self) -> Result<(), Error>;
894}
895
896#[derive(thiserror::Error, Debug, PartialEq, Eq)]
898pub enum Error {
899 #[error("Validation error: {0}")]
900 ValidationError(String),
901 #[error("invalid country code: {0}")]
902 InvalidCountryCode(String),
903}
904
905impl From<&str> for Error {
906 fn from(value: &str) -> Self {
907 Self::ValidationError(value.to_owned())
908 }
909}
910
911#[cfg(test)]
912mod tests {
913 use super::*;
914 use serde_test::{assert_tokens, Token};
915
916 impl NaturalPerson {
917 fn mock() -> Self {
918 Self {
919 name: NaturalPersonName::mock().into(),
920 geographic_address: None.into(),
921 national_identification: None,
922 customer_identification: None,
923 date_and_place_of_birth: None,
924 country_of_residence: None,
925 }
926 }
927 }
928
929 impl LegalPerson {
930 fn mock() -> Self {
931 Self {
932 name: LegalPersonName::mock(),
933 geographic_address: None.into(),
934 customer_identification: None,
935 national_identification: None,
936 country_of_registration: None,
937 }
938 }
939 }
940
941 impl LegalPersonName {
942 fn mock() -> Self {
943 Self {
944 name_identifier: LegalPersonNameID::mock().into(),
945 local_name_identifier: None.into(),
946 phonetic_name_identifier: None.into(),
947 }
948 }
949 }
950
951 impl LegalPersonNameID {
952 fn mock() -> Self {
953 Self {
954 legal_person_name: "Company A".try_into().unwrap(),
955 legal_person_name_identifier_type: LegalPersonNameTypeCode::Legal,
956 }
957 }
958 }
959
960 impl NationalIdentification {
961 fn mock() -> Self {
962 Self {
963 national_identifier: "id".try_into().unwrap(),
964 national_identifier_type: NationalIdentifierTypeCode::Unspecified,
965 country_of_issue: None,
966 registration_authority: Some("RA000001".try_into().unwrap()),
967 }
968 }
969 }
970
971 impl Address {
972 fn mock() -> Self {
973 Self {
974 address_type: AddressTypeCode::Residential,
975 department: None,
976 sub_department: None,
977 street_name: None,
978 building_number: None,
979 building_name: None,
980 floor: None,
981 post_box: None,
982 room: None,
983 post_code: None,
984 town_name: "Zurich".try_into().unwrap(),
985 town_location_name: None,
986 district_name: None,
987 country_sub_division: None,
988 address_line: Some("Main street".try_into().unwrap()).into(),
989 country: "CH".try_into().unwrap(),
990 }
991 }
992 }
993
994 impl NaturalPersonNameID {
995 fn mock() -> Self {
996 Self {
997 primary_identifier: "Engels".try_into().unwrap(),
998 secondary_identifier: Some("Friedrich".try_into().unwrap()),
999 name_identifier_type: NaturalPersonNameTypeCode::LegalName,
1000 }
1001 }
1002 }
1003
1004 impl NaturalPersonName {
1005 fn mock() -> Self {
1006 Self {
1007 name_identifier: NaturalPersonNameID::mock().into(),
1008 local_name_identifier: None.into(),
1009 phonetic_name_identifier: None.into(),
1010 }
1011 }
1012 }
1013
1014 impl DateAndPlaceOfBirth {
1015 fn mock() -> Self {
1016 Self {
1017 date_of_birth: chrono::NaiveDate::from_ymd_opt(1946, 11, 5).unwrap(),
1018 place_of_birth: "London".try_into().unwrap(),
1019 }
1020 }
1021 }
1022
1023 #[test]
1024 fn test_date() {
1025 assert_tokens(
1026 &Date::from_ymd_opt(2018, 11, 5).unwrap(),
1027 &[Token::String("2018-11-05")],
1028 );
1029 }
1030
1031 #[test]
1032 fn test_type_codes() {
1033 assert_tokens(
1034 &NaturalPersonNameTypeCode::Alias,
1035 &[Token::UnitVariant {
1036 name: "NaturalPersonNameTypeCode",
1037 variant: "ALIA",
1038 }],
1039 );
1040 assert_tokens(
1041 &LegalPersonNameTypeCode::Legal,
1042 &[Token::UnitVariant {
1043 name: "LegalPersonNameTypeCode",
1044 variant: "LEGL",
1045 }],
1046 );
1047 assert_tokens(
1048 &AddressTypeCode::Business,
1049 &[Token::UnitVariant {
1050 name: "AddressTypeCode",
1051 variant: "BIZZ",
1052 }],
1053 );
1054 assert_tokens(
1055 &NationalIdentifierTypeCode::AlienRegistrationNumber,
1056 &[Token::UnitVariant {
1057 name: "NationalIdentifierTypeCode",
1058 variant: "ARNU",
1059 }],
1060 );
1061 }
1062
1063 fn match_validation_error(val: &impl Validatable, code: u8) {
1064 let res = val.validate();
1065 assert!(res
1066 .unwrap_err()
1067 .to_string()
1068 .ends_with(format!("(IVMS101 C{code})").as_str()));
1069 }
1070
1071 #[test]
1072 fn test_person_serialization() {
1073 let person = Person::NaturalPerson(NaturalPerson::mock());
1074 let serialized = serde_json::to_string(&person).unwrap();
1075 assert_eq!(
1076 serialized,
1077 r#"{"naturalPerson":{"name":{"nameIdentifier":{"primaryIdentifier":"Engels","secondaryIdentifier":"Friedrich","nameIdentifierType":"LEGL"}}}}"#
1078 );
1079 let deserialized: Person = serde_json::from_str(&serialized).unwrap();
1080 assert_eq!(person, deserialized);
1081
1082 let person = Person::LegalPerson(LegalPerson::mock());
1083 let serialized = serde_json::to_string(&person).unwrap();
1084 assert_eq!(
1085 serialized,
1086 r#"{"legalPerson":{"name":{"nameIdentifier":{"legalPersonName":"Company A","legalPersonNameIdentifierType":"LEGL"}}}}"#
1087 );
1088 let deserialized: Person = serde_json::from_str(&serialized).unwrap();
1089 assert_eq!(person, deserialized);
1090 }
1091
1092 #[test]
1093 fn test_c1_validation_error() {
1094 let originator = Originator {
1095 originator_persons: Person::NaturalPerson(NaturalPerson::mock()).into(),
1096 account_number: None.into(),
1097 };
1098 match_validation_error(&originator, 1);
1099 }
1100
1101 #[test]
1102 fn test_c1_validation_pass() {
1103 let mut person = NaturalPerson::mock();
1104 person.geographic_address = Some(Address::mock()).into();
1105 let originator = Originator {
1106 originator_persons: Person::NaturalPerson(person.clone()).into(),
1107 account_number: None.into(),
1108 };
1109 originator.validate().unwrap();
1110
1111 person.geographic_address = None.into();
1112 person.customer_identification = Some("customer-id".try_into().unwrap());
1113 let originator = Originator {
1114 originator_persons: Person::NaturalPerson(person.clone()).into(),
1115 account_number: None.into(),
1116 };
1117 originator.validate().unwrap();
1118
1119 person.customer_identification = None;
1120 person.national_identification = Some(NationalIdentification::mock());
1121 let originator = Originator {
1122 originator_persons: Person::NaturalPerson(person.clone()).into(),
1123 account_number: None.into(),
1124 };
1125 originator.validate().unwrap();
1126
1127 person.national_identification = None;
1128 person.date_and_place_of_birth = Some(DateAndPlaceOfBirth::mock());
1129 let originator = Originator {
1130 originator_persons: Person::NaturalPerson(person).into(),
1131 account_number: None.into(),
1132 };
1133 originator.validate().unwrap();
1134
1135 let beneficiary = Beneficiary {
1136 beneficiary_persons: Person::NaturalPerson(NaturalPerson::mock()).into(),
1137 account_number: None.into(),
1138 };
1139 beneficiary.validate().unwrap();
1140 }
1141
1142 #[test]
1143 fn test_c2_validation_error() {
1144 let date = DateAndPlaceOfBirth {
1145 date_of_birth: chrono::NaiveDate::MAX,
1146 place_of_birth: "Bern".try_into().unwrap(),
1147 };
1148 match_validation_error(&date, 2);
1149 }
1150
1151 #[test]
1152 fn test_c2_validation_pass() {
1153 let date = DateAndPlaceOfBirth {
1154 date_of_birth: chrono::NaiveDate::MIN,
1155 place_of_birth: "Bern".try_into().unwrap(),
1156 };
1157
1158 date.validate().unwrap();
1159 }
1160
1161 #[test]
1164 fn test_c4_validation_error() {
1165 let legal = LegalPerson::mock();
1166 match_validation_error(&legal, 4);
1167 }
1168
1169 #[test]
1170 fn test_c4_validation_pass() {
1171 let mut legal = LegalPerson::mock();
1172
1173 legal.geographic_address = Some(Address::mock()).into();
1174 legal.validate().unwrap();
1175 legal.geographic_address = None.into();
1176
1177 legal.customer_identification = Some("id".try_into().unwrap());
1178 legal.validate().unwrap();
1179 legal.customer_identification = None;
1180
1181 legal.national_identification = Some(NationalIdentification::mock());
1182 legal.validate().unwrap();
1183 }
1184
1185 #[test]
1186 fn test_c5_validation_error() {
1187 let mut legal = LegalPersonName::mock();
1188 legal.name_identifier = LegalPersonNameID {
1189 legal_person_name: "Company A".try_into().unwrap(),
1190 legal_person_name_identifier_type: LegalPersonNameTypeCode::Short,
1191 }
1192 .into();
1193 match_validation_error(&legal, 5);
1194 }
1195
1196 #[test]
1197 fn test_c5_validation_pass() {
1198 let legal = LegalPersonName::mock();
1199 legal.validate().unwrap();
1200 }
1201
1202 #[test]
1203 fn test_c6_validation_error() {
1204 let mut name = NaturalPersonName::mock();
1205 name.name_identifier = NaturalPersonNameID {
1206 primary_identifier: "Karl".try_into().unwrap(),
1207 name_identifier_type: NaturalPersonNameTypeCode::Alias,
1208 secondary_identifier: None,
1209 }
1210 .into();
1211 match_validation_error(&name, 6);
1212 }
1213
1214 #[test]
1215 fn test_c6_validation_pass() {
1216 let mut name = NaturalPersonName::mock();
1217 name.name_identifier = NaturalPersonNameID {
1218 primary_identifier: "Emil Steinberger".try_into().unwrap(),
1219 secondary_identifier: None,
1220 name_identifier_type: NaturalPersonNameTypeCode::LegalName,
1221 }
1222 .into();
1223 name.validate().unwrap();
1224 }
1225
1226 #[test]
1227 fn test_c7_validation_error() {
1228 let mut person = LegalPerson::mock();
1229 let mut id = NationalIdentification::mock();
1230
1231 for code in [
1232 NationalIdentifierTypeCode::AlienRegistrationNumber,
1233 NationalIdentifierTypeCode::PassportNumber,
1234 NationalIdentifierTypeCode::DriverLicenseNumber,
1235 NationalIdentifierTypeCode::ForeignInvestmentIdentityNumber,
1236 NationalIdentifierTypeCode::IdentityCardNumber,
1237 NationalIdentifierTypeCode::SocialSecurityNumber,
1238 ] {
1239 id.national_identifier_type = code;
1240 person.national_identification = Some(id.clone());
1241 match_validation_error(&person, 7);
1242 }
1243 }
1244
1245 #[test]
1246 fn test_c7_validation_pass() {
1247 let mut person = LegalPerson::mock();
1248
1249 for code in [
1250 NationalIdentifierTypeCode::LegalEntityIdentifier,
1251 NationalIdentifierTypeCode::Unspecified,
1252 NationalIdentifierTypeCode::RegistrationAuthorityIdentifier,
1253 NationalIdentifierTypeCode::TaxIdentificationNumber,
1254 ] {
1255 let mut id = NationalIdentification::mock();
1256 id.national_identifier_type = code.clone();
1257 if code == NationalIdentifierTypeCode::LegalEntityIdentifier {
1258 id.national_identifier = "2594007XIACKNMUAW223".try_into().unwrap();
1260 id.registration_authority = None;
1262 }
1263 person.national_identification = Some(id.clone());
1264 person.validate().unwrap();
1265 }
1266 }
1267
1268 #[test]
1269 fn test_c8_validation_error() {
1270 let mut addr = Address::mock();
1271 addr.address_line = None.into();
1272 match_validation_error(&addr, 8);
1273
1274 addr.street_name = Some("main street".try_into().unwrap());
1275 match_validation_error(&addr, 8);
1276 }
1277
1278 #[test]
1279 fn test_c8_validation_pass() {
1280 let mut addr = Address::mock();
1281 addr.validate().unwrap();
1282
1283 addr.address_line = None.into();
1284 addr.street_name = Some("main street".try_into().unwrap());
1285 addr.building_name = Some("main building".try_into().unwrap());
1286 addr.validate().unwrap();
1287
1288 addr.building_name = None;
1289 addr.building_number = Some("12".try_into().unwrap());
1290 addr.validate().unwrap();
1291 }
1292
1293 #[test]
1294 fn test_c9_validation_error() {
1295 let mut ni = NationalIdentification::mock();
1296 ni.country_of_issue = Some("CH".try_into().unwrap());
1297 let mut person = LegalPerson::mock();
1298 person.national_identification = Some(ni.clone());
1299 match_validation_error(&person, 9);
1300
1301 ni.national_identifier_type = NationalIdentifierTypeCode::LegalEntityIdentifier;
1302 ni.national_identifier = "2594007XIACKNMUAW223".try_into().unwrap();
1304 person.national_identification = Some(ni.clone());
1305 match_validation_error(&person, 9);
1306
1307 ni.national_identifier_type = NationalIdentifierTypeCode::Unspecified;
1308 ni.registration_authority = None;
1309 person.national_identification = Some(ni);
1310 match_validation_error(&person, 9);
1311 }
1312
1313 #[test]
1314 fn test_c9_validation_pass() {
1315 let mut person = LegalPerson::mock();
1316 person.customer_identification = Some("id".try_into().unwrap());
1317 person.validate().unwrap();
1318
1319 let mut ni = NationalIdentification::mock();
1320 person.national_identification = Some(ni.clone());
1321 person.validate().unwrap();
1322
1323 ni.registration_authority = None;
1324 ni.national_identifier_type = NationalIdentifierTypeCode::LegalEntityIdentifier;
1325 ni.national_identifier = "2594007XIACKNMUAW223".try_into().unwrap();
1327 person.national_identification = Some(ni);
1328 person.validate().unwrap();
1329 }
1330
1331 #[test]
1334 fn test_c11_validation_error() {
1335 let mut person = LegalPerson::mock();
1336 let mut ni = NationalIdentification::mock();
1337 ni.registration_authority = None;
1338 ni.national_identifier_type = NationalIdentifierTypeCode::LegalEntityIdentifier;
1339 ni.national_identifier = "invalid-lei".try_into().unwrap();
1340 person.national_identification = Some(ni);
1341 match_validation_error(&person, 11);
1342 }
1343
1344 #[test]
1345 fn test_c11_validation_pass() {
1346 let mut person = LegalPerson::mock();
1347 let mut ni = NationalIdentification::mock();
1348 ni.registration_authority = None;
1349 ni.national_identifier_type = NationalIdentifierTypeCode::LegalEntityIdentifier;
1350 ni.national_identifier = "2594007XIACKNMUAW223".try_into().unwrap();
1351 person.national_identification = Some(ni);
1352 person.validate().unwrap();
1353 }
1354
1355 #[test]
1356 fn test_natural_person_name() {
1357 let mut person = NaturalPerson::mock();
1358 assert_eq!(person.first_name(), Some("Friedrich".into()));
1359 assert_eq!(person.last_name(), "Engels");
1360 let mut name = NaturalPersonNameID::mock();
1361 name.secondary_identifier = None;
1362 person.name = NaturalPersonName {
1363 name_identifier: name.into(),
1364 local_name_identifier: None.into(),
1365 phonetic_name_identifier: None.into(),
1366 }
1367 .into();
1368 assert_eq!(person.first_name(), None);
1369 assert_eq!(person.last_name(), "Engels".to_string());
1370 }
1371
1372 #[test]
1373 fn test_legal_person_name() {
1374 assert_eq!(LegalPerson::mock().name(), "Company A");
1375 }
1376
1377 #[test]
1378 fn test_address_display() {
1379 let person = NaturalPerson::mock();
1380 assert_eq!(person.address(), None);
1381 let mut address = Address::mock();
1382 assert_eq!(
1383 address.to_string(),
1384 "Main street, Zurich, Switzerland".to_string()
1385 );
1386 address.post_code = Some("8000".try_into().unwrap());
1387 assert_eq!(
1388 address.to_string(),
1389 "Main street, 8000 Zurich, Switzerland".to_string()
1390 );
1391 address.address_line =
1392 vec!["line 1".try_into().unwrap(), "line 2".try_into().unwrap()].into();
1393 assert_eq!(
1394 address.to_string(),
1395 "line 1, line 2, 8000 Zurich, Switzerland".to_string()
1396 );
1397 address.address_line = None.into();
1398 assert_eq!(address.to_string(), "8000 Zurich, Switzerland".to_string());
1399 address.street_name = Some("Main street".try_into().unwrap());
1400 address.building_number = Some("12".try_into().unwrap());
1401 assert_eq!(
1402 address.to_string(),
1403 "Main street 12, 8000 Zurich, Switzerland".to_string()
1404 );
1405 }
1406}