swift_mt_message/fields/
field79.rs

1use super::swift_utils::parse_swift_chars;
2use crate::errors::ParseError;
3use crate::traits::SwiftField;
4use serde::{Deserialize, Serialize};
5
6/// **Field 79: Narrative**
7///
8/// Extended narrative information for detailed transaction descriptions and explanations.
9///
10/// **Format:** `35*50x` (max 35 lines, 50 chars each, total 1750 chars)
11/// **Used in:** MT 199, 299 (free format messages), MT 196, 296 (answer messages)
12///
13/// **Example:**
14/// ```text
15/// :79:PAYMENT DETAILS:
16/// INVOICE NUMBER: 12345
17/// SERVICES PROVIDED: CONSULTING
18/// PERIOD: DECEMBER 2023
19/// ```
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct Field79 {
23    /// Extended narrative information (max 35 lines, 50 chars each)
24    pub information: Vec<String>,
25}
26
27impl SwiftField for Field79 {
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 35 lines of 50 characters each
35        for line in input.lines().take(35) {
36            // Validate line length (max 50 characters)
37            if line.len() > 50 {
38                return Err(ParseError::InvalidFormat {
39                    message: format!("Field 79 line exceeds 50 characters: {}", line.len()),
40                });
41            }
42
43            // Validate SWIFT character set
44            parse_swift_chars(line, "Field 79 line")?;
45
46            lines.push(line.to_string());
47        }
48
49        if lines.is_empty() {
50            return Err(ParseError::InvalidFormat {
51                message: "Field 79 must contain at least one line".to_string(),
52            });
53        }
54
55        Ok(Field79 { information: lines })
56    }
57
58    fn to_swift_string(&self) -> String {
59        let content = self.information.join("\n");
60        format!(":79:{}", content)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn test_field79_parse_single_line() {
70        let field = Field79::parse("PAYMENT FOR INVOICE 12345 DATED 2023-12-01").unwrap();
71        assert_eq!(field.information.len(), 1);
72        assert_eq!(
73            field.information[0],
74            "PAYMENT FOR INVOICE 12345 DATED 2023-12-01"
75        );
76    }
77
78    #[test]
79    fn test_field79_parse_multiple_lines() {
80        let input = "PAYMENT DETAILS:\nINVOICE NUMBER: 12345\nSERVICES PROVIDED: CONSULTING\nPERIOD: DECEMBER 2023";
81        let field = Field79::parse(input).unwrap();
82        assert_eq!(field.information.len(), 4);
83        assert_eq!(field.information[0], "PAYMENT DETAILS:");
84        assert_eq!(field.information[1], "INVOICE NUMBER: 12345");
85        assert_eq!(field.information[2], "SERVICES PROVIDED: CONSULTING");
86        assert_eq!(field.information[3], "PERIOD: DECEMBER 2023");
87    }
88
89    #[test]
90    fn test_field79_line_too_long() {
91        let long_line = "THIS LINE IS MUCH TOO LONG TO BE ACCEPTED IN FIELD 79 AS IT EXCEEDS THE 50 CHARACTER LIMIT";
92        assert!(Field79::parse(long_line).is_err());
93    }
94
95    #[test]
96    fn test_field79_max_line_length() {
97        // Exactly 50 characters should work
98        let line_50_chars = "12345678901234567890123456789012345678901234567890";
99        let field = Field79::parse(line_50_chars).unwrap();
100        assert_eq!(field.information[0], line_50_chars);
101    }
102
103    #[test]
104    fn test_field79_empty_input() {
105        assert!(Field79::parse("").is_err());
106    }
107
108    #[test]
109    fn test_field79_to_swift_string() {
110        let field = Field79 {
111            information: vec![
112                "TRANSACTION DESCRIPTION:".to_string(),
113                "PAYMENT FOR SERVICES".to_string(),
114                "INVOICE: 2023-12345".to_string(),
115            ],
116        };
117        let expected = ":79:TRANSACTION DESCRIPTION:\nPAYMENT FOR SERVICES\nINVOICE: 2023-12345";
118        assert_eq!(field.to_swift_string(), expected);
119    }
120
121    #[test]
122    fn test_field79_single_line_to_swift_string() {
123        let field = Field79 {
124            information: vec!["SINGLE LINE NARRATIVE".to_string()],
125        };
126        assert_eq!(field.to_swift_string(), ":79:SINGLE LINE NARRATIVE");
127    }
128
129    #[test]
130    fn test_field79_max_lines() {
131        let mut lines = Vec::new();
132        for i in 1..=35 {
133            lines.push(format!("LINE {}", i));
134        }
135        let input = lines.join("\n");
136        let field = Field79::parse(&input).unwrap();
137        assert_eq!(field.information.len(), 35);
138        assert_eq!(field.information[0], "LINE 1");
139        assert_eq!(field.information[34], "LINE 35");
140    }
141}