swift_mt_message/fields/
field70.rs

1use super::field_utils::parse_multiline_text;
2use crate::errors::ParseError;
3use crate::traits::SwiftField;
4use serde::{Deserialize, Serialize};
5
6/// **Field 70: Remittance Information**
7///
8/// Payment details and references transmitted to beneficiary for
9/// transaction identification and reconciliation.
10///
11/// **Format:** `4*35x` (max 4 lines, 35 chars each)
12/// **Common codes:** `/INV/` (invoice), `/RFB/` (reference for beneficiary), `/ROC/` (reference of customer)
13///
14/// **Example:**
15/// ```text
16/// :70:/INV/20231215/INV-12345
17/// PAYMENT FOR SERVICES
18/// ```
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub struct Field70 {
21    /// Remittance narrative (max 4 lines, 35 chars each)
22    pub narrative: Vec<String>,
23}
24
25impl SwiftField for Field70 {
26    fn parse(input: &str) -> crate::Result<Self>
27    where
28        Self: Sized,
29    {
30        // Parse as multiline text (up to 4 lines, 35 chars each)
31        let narrative = parse_multiline_text(input, 4, 35)?;
32
33        if narrative.is_empty() {
34            return Err(ParseError::InvalidFormat {
35                message: "Field 70 must have at least one line of narrative".to_string(),
36            });
37        }
38
39        Ok(Field70 { narrative })
40    }
41
42    fn to_swift_string(&self) -> String {
43        format!(":70:{}", self.narrative.join("\n"))
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn test_field70_single_line() {
53        let field = Field70::parse("PAYMENT FOR INVOICE 12345").unwrap();
54        assert_eq!(field.narrative.len(), 1);
55        assert_eq!(field.narrative[0], "PAYMENT FOR INVOICE 12345");
56    }
57
58    #[test]
59    fn test_field70_multiline() {
60        let input = "/INV/123456\nPAYMENT FOR GOODS\nDELIVERED ON 2024-07-19\nREF: CONTRACT-001";
61        let field = Field70::parse(input).unwrap();
62        assert_eq!(field.narrative.len(), 4);
63        assert_eq!(field.narrative[0], "/INV/123456");
64        assert_eq!(field.narrative[1], "PAYMENT FOR GOODS");
65        assert_eq!(field.narrative[2], "DELIVERED ON 2024-07-19");
66        assert_eq!(field.narrative[3], "REF: CONTRACT-001");
67    }
68
69    #[test]
70    fn test_field70_to_swift_string() {
71        let field = Field70 {
72            narrative: vec!["LINE ONE".to_string(), "LINE TWO".to_string()],
73        };
74        assert_eq!(field.to_swift_string(), ":70:LINE ONE\nLINE TWO");
75    }
76
77    #[test]
78    fn test_field70_max_lines() {
79        let input = "LINE1\nLINE2\nLINE3\nLINE4";
80        let field = Field70::parse(input).unwrap();
81        assert_eq!(field.narrative.len(), 4);
82
83        // Test that 5 lines would fail
84        let too_many = "LINE1\nLINE2\nLINE3\nLINE4\nLINE5";
85        assert!(Field70::parse(too_many).is_err());
86    }
87
88    #[test]
89    fn test_field70_line_length() {
90        // Test max length line (35 chars)
91        let max_line = "A".repeat(35);
92        let field = Field70::parse(&max_line).unwrap();
93        assert_eq!(field.narrative[0].len(), 35);
94
95        // Test too long line (36 chars)
96        let too_long = "A".repeat(36);
97        assert!(Field70::parse(&too_long).is_err());
98    }
99}