swift_mt_message/messages/
mt292.rs

1use crate::errors::{ParseError, SwiftValidationError};
2use crate::fields::*;
3use crate::parser::MessageParser;
4use crate::parser::utils::*;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// **MT292: Request for Cancellation**
9///
10/// Request to cancel previously sent message.
11///
12/// **Usage:** Payment cancellation requests
13/// **Category:** Category 2 (Financial Institution Transfers)
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15pub struct MT292 {
16    /// Transaction Reference Number (Field 20)
17    #[serde(rename = "20")]
18    pub field_20: Field20,
19
20    /// Related Reference (Field 21)
21    #[serde(rename = "21")]
22    pub field_21: Field21NoOption,
23
24    /// MT and Date of the Original Message (Field 11S)
25    #[serde(rename = "11S")]
26    pub field_11s: Field11S,
27
28    /// Narrative Description of Original Message (Field 79)
29    #[serde(rename = "79", skip_serializing_if = "Option::is_none")]
30    pub field_79: Option<Field79>,
31
32    /// Copy of mandatory fields from original message
33    #[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
34    pub original_fields: HashMap<String, serde_json::Value>,
35}
36
37impl MT292 {
38    /// Parse MT292 from a raw SWIFT message string
39    pub fn parse_from_block4(block4: &str) -> Result<Self, ParseError> {
40        let mut parser = MessageParser::new(block4, "292");
41
42        // Parse mandatory fields
43        let field_20 = parser.parse_field::<Field20>("20")?;
44        let field_21 = parser.parse_field::<Field21NoOption>("21")?;
45        let field_11s = parser.parse_field::<Field11S>("11S")?;
46
47        // Parse optional/conditional Field 79
48        let field_79 = parser.parse_optional_field::<Field79>("79")?;
49
50        // Collect any remaining fields as original message fields
51        // This would need to be implemented in MessageParser but for now use empty HashMap
52        let original_fields = HashMap::new();
53
54        // Validation: Either Field 79 or original fields must be present
55        if field_79.is_none() && original_fields.is_empty() {
56            return Err(ParseError::InvalidFormat {
57                message:
58                    "MT292: Either Field 79 or copy of original message fields must be present"
59                        .to_string(),
60            });
61        }
62
63        Ok(MT292 {
64            field_20,
65            field_21,
66            field_11s,
67            field_79,
68            original_fields,
69        })
70    }
71
72    // ========================================================================
73    // NETWORK VALIDATION RULES (SR 2025 MT292)
74    // ========================================================================
75
76    // ========================================================================
77    // HELPER METHODS
78    // ========================================================================
79
80    /// Check if field 79 is present
81    fn has_field_79(&self) -> bool {
82        self.field_79.is_some()
83    }
84
85    /// Check if copy of original message fields is present
86    fn has_original_fields(&self) -> bool {
87        !self.original_fields.is_empty()
88    }
89
90    // ========================================================================
91    // VALIDATION RULES (C1)
92    // ========================================================================
93
94    /// C1: Field 79 or Copy of Mandatory Fields Requirement (Error code: C25)
95    /// Field 79 or a copy of at least the mandatory fields of the original message
96    /// or both must be present
97    fn validate_c1_field_79_or_original_fields(&self) -> Option<SwiftValidationError> {
98        let has_79 = self.has_field_79();
99        let has_original = self.has_original_fields();
100
101        if !has_79 && !has_original {
102            return Some(SwiftValidationError::content_error(
103                "C25",
104                "79",
105                "",
106                "Field 79 (Narrative Description) or a copy of at least the mandatory fields of the original message must be present",
107                "Either field 79 or a copy of at least the mandatory fields of the original message or both must be present",
108            ));
109        }
110
111        None
112    }
113
114    /// Main validation method - validates all network rules
115    /// Returns array of validation errors, respects stop_on_first_error flag
116    pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
117        let mut all_errors = Vec::new();
118
119        // C1: Field 79 or Copy of Mandatory Fields Requirement
120        if let Some(error) = self.validate_c1_field_79_or_original_fields() {
121            all_errors.push(error);
122            if stop_on_first_error {
123                return all_errors;
124            }
125        }
126
127        all_errors
128    }
129}
130
131impl crate::traits::SwiftMessageBody for MT292 {
132    fn message_type() -> &'static str {
133        "292"
134    }
135
136    fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
137        Self::parse_from_block4(block4)
138    }
139
140    fn to_mt_string(&self) -> String {
141        let mut result = String::new();
142
143        // Add mandatory fields in the correct SWIFT order
144        append_field(&mut result, &self.field_20);
145        append_field(&mut result, &self.field_21);
146        append_field(&mut result, &self.field_11s);
147
148        // Add optional field 79
149        append_optional_field(&mut result, &self.field_79);
150
151        finalize_mt_string(result, false)
152    }
153
154    fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
155        // Call the existing public method implementation
156        MT292::validate_network_rules(self, stop_on_first_error)
157    }
158}