swift_mt_message/fields/
field86.rs

1use super::swift_utils::parse_swift_chars;
2use crate::errors::ParseError;
3use crate::traits::SwiftField;
4use serde::{Deserialize, Serialize};
5
6/// **Field 86: Information to Account Owner**
7///
8/// Additional information to account owner regarding transactions or account activities
9/// in statement messages.
10///
11/// **Format:** `6*65x` (max 6 lines, 65 chars each)
12/// **Used in:** MT 940 (customer statement), MT 942 (interim transaction report)
13///
14/// **Example:**
15/// ```text
16/// :86:WIRE TRANSFER RECEIVED
17/// FROM: INTERNATIONAL BANK
18/// PURPOSE: TRADE SETTLEMENT
19/// ```
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct Field86 {
23    /// Information narrative for account owner (max 6 lines, 65 chars each)
24    pub narrative: Vec<String>,
25}
26
27impl SwiftField for Field86 {
28    fn parse(input: &str) -> crate::Result<Self>
29    where
30        Self: Sized,
31    {
32        let mut lines = Vec::new();
33
34        // Parse up to 6 lines of 65 characters each
35        for line in input.lines().take(6) {
36            // Validate line length (max 65 characters)
37            if line.len() > 65 {
38                return Err(ParseError::InvalidFormat {
39                    message: format!("Field 86 line exceeds 65 characters: {}", line.len()),
40                });
41            }
42
43            // Validate SWIFT character set
44            parse_swift_chars(line, "Field 86 line")?;
45
46            lines.push(line.to_string());
47        }
48
49        if lines.is_empty() {
50            return Err(ParseError::InvalidFormat {
51                message: "Field 86 must contain at least one line".to_string(),
52            });
53        }
54
55        Ok(Field86 { narrative: lines })
56    }
57
58    fn to_swift_string(&self) -> String {
59        let content = self.narrative.join("\n");
60        format!(":86:{}", content)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn test_field86_parse_single_line() {
70        let field = Field86::parse("PAYMENT RECEIVED FROM ABC COMPANY FOR INVOICE 12345").unwrap();
71        assert_eq!(field.narrative.len(), 1);
72        assert_eq!(
73            field.narrative[0],
74            "PAYMENT RECEIVED FROM ABC COMPANY FOR INVOICE 12345"
75        );
76    }
77
78    #[test]
79    fn test_field86_parse_multiple_lines() {
80        let input = "TRANSACTION DETAILS:\nCOUNTERPARTY: ABC CORP\nREFERENCE: PAY-2023-12345\nDESCRIPTION: MONTHLY SERVICE FEE";
81        let field = Field86::parse(input).unwrap();
82        assert_eq!(field.narrative.len(), 4);
83        assert_eq!(field.narrative[0], "TRANSACTION DETAILS:");
84        assert_eq!(field.narrative[1], "COUNTERPARTY: ABC CORP");
85        assert_eq!(field.narrative[2], "REFERENCE: PAY-2023-12345");
86        assert_eq!(field.narrative[3], "DESCRIPTION: MONTHLY SERVICE FEE");
87    }
88
89    #[test]
90    fn test_field86_line_too_long() {
91        let long_line = "THIS LINE IS MUCH TOO LONG TO BE ACCEPTED IN FIELD 86 AS IT EXCEEDS THE 65 CHARACTER LIMIT SIGNIFICANTLY";
92        assert!(Field86::parse(long_line).is_err());
93    }
94
95    #[test]
96    fn test_field86_max_line_length() {
97        // Exactly 65 characters should work
98        let line_65_chars = "12345678901234567890123456789012345678901234567890123456789012345";
99        let field = Field86::parse(line_65_chars).unwrap();
100        assert_eq!(field.narrative[0], line_65_chars);
101    }
102
103    #[test]
104    fn test_field86_max_lines() {
105        let input = "LINE 1\nLINE 2\nLINE 3\nLINE 4\nLINE 5\nLINE 6";
106        let field = Field86::parse(input).unwrap();
107        assert_eq!(field.narrative.len(), 6);
108        assert_eq!(field.narrative[0], "LINE 1");
109        assert_eq!(field.narrative[5], "LINE 6");
110    }
111
112    #[test]
113    fn test_field86_empty_input() {
114        assert!(Field86::parse("").is_err());
115    }
116
117    #[test]
118    fn test_field86_to_swift_string() {
119        let field = Field86 {
120            narrative: vec![
121                "WIRE TRANSFER RECEIVED".to_string(),
122                "FROM: INTERNATIONAL BANK".to_string(),
123                "PURPOSE: TRADE SETTLEMENT".to_string(),
124            ],
125        };
126        let expected =
127            ":86:WIRE TRANSFER RECEIVED\nFROM: INTERNATIONAL BANK\nPURPOSE: TRADE SETTLEMENT";
128        assert_eq!(field.to_swift_string(), expected);
129    }
130
131    #[test]
132    fn test_field86_single_line_to_swift_string() {
133        let field = Field86 {
134            narrative: vec!["DIRECT DEBIT AUTHORIZATION PAYMENT".to_string()],
135        };
136        assert_eq!(
137            field.to_swift_string(),
138            ":86:DIRECT DEBIT AUTHORIZATION PAYMENT"
139        );
140    }
141
142    #[test]
143    fn test_field86_account_holder_information() {
144        let field = Field86 {
145            narrative: vec![
146                "ACCOUNT BALANCE UPDATE".to_string(),
147                "INTEREST CREDITED: USD 125.50".to_string(),
148                "ANNUAL RATE: 2.5%".to_string(),
149                "CALCULATION PERIOD: Q4 2023".to_string(),
150            ],
151        };
152
153        let expected = ":86:ACCOUNT BALANCE UPDATE\nINTEREST CREDITED: USD 125.50\nANNUAL RATE: 2.5%\nCALCULATION PERIOD: Q4 2023";
154        assert_eq!(field.to_swift_string(), expected);
155        assert_eq!(field.narrative.len(), 4);
156    }
157}