swift_mt_message/fields/
field58.rs1use super::swift_utils::{parse_bic, parse_swift_chars};
2use crate::errors::ParseError;
3use crate::traits::SwiftField;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub struct Field58A {
21 pub party_identifier: Option<String>,
23
24 pub bic: String,
26}
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35pub struct Field58D {
36 pub party_identifier: Option<String>,
38
39 pub name_and_address: Vec<String>,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub enum Field58 {
45 #[serde(rename = "58A")]
46 A(Field58A),
47 #[serde(rename = "58D")]
48 D(Field58D),
49}
50
51impl SwiftField for Field58A {
52 fn parse(input: &str) -> crate::Result<Self>
53 where
54 Self: Sized,
55 {
56 let lines: Vec<&str> = input.lines().collect();
57
58 let mut party_identifier = None;
59 let mut bic_line_idx = 0;
60
61 if !lines.is_empty() && lines[0].starts_with('/') {
63 party_identifier = Some(lines[0][1..].to_string()); bic_line_idx = 1;
65 }
66
67 if bic_line_idx >= lines.len() {
69 return Err(ParseError::InvalidFormat {
70 message: "Field 58A missing BIC code".to_string(),
71 });
72 }
73
74 let bic = parse_bic(lines[bic_line_idx])?;
75
76 Ok(Field58A {
77 party_identifier,
78 bic,
79 })
80 }
81
82 fn to_swift_string(&self) -> String {
83 let mut result = ":58A:".to_string();
84
85 if let Some(ref party_id) = self.party_identifier {
86 result.push('/'); result.push_str(party_id);
88 result.push('\n');
89 }
90
91 result.push_str(&self.bic);
92 result
93 }
94}
95
96impl SwiftField for Field58D {
97 fn parse(input: &str) -> crate::Result<Self>
98 where
99 Self: Sized,
100 {
101 let mut lines = input.lines().collect::<Vec<_>>();
102 let mut party_identifier = None;
103
104 if let Some(first_line) = lines.first() {
108 if first_line.starts_with('/') && first_line.len() <= 35 && lines.len() > 1 {
110 party_identifier = Some(first_line[1..].to_string());
112 lines.remove(0);
113 }
114 }
115
116 let mut name_and_address = Vec::new();
118 for (i, line) in lines.iter().enumerate() {
119 if i >= 4 {
120 break;
121 }
122 if line.len() > 35 {
123 return Err(ParseError::InvalidFormat {
124 message: format!("Field 58D line {} exceeds 35 characters", i + 1),
125 });
126 }
127 parse_swift_chars(line, &format!("Field 58D line {}", i + 1))?;
128 name_and_address.push(line.to_string());
129 }
130
131 if name_and_address.is_empty() {
132 return Err(ParseError::InvalidFormat {
133 message: "Field 58D must contain name and address information".to_string(),
134 });
135 }
136
137 Ok(Field58D {
138 party_identifier,
139 name_and_address,
140 })
141 }
142
143 fn to_swift_string(&self) -> String {
144 let mut result = ":58D:".to_string();
145
146 if let Some(ref party_id) = self.party_identifier {
147 result.push('/'); result.push_str(party_id);
149 result.push('\n');
150 }
151
152 result.push_str(&self.name_and_address.join("\n"));
153 result
154 }
155}
156
157impl SwiftField for Field58 {
158 fn parse(input: &str) -> crate::Result<Self>
159 where
160 Self: Sized,
161 {
162 if let Ok(field) = Field58A::parse(input) {
164 return Ok(Field58::A(field));
165 }
166
167 if let Ok(field) = Field58D::parse(input) {
169 return Ok(Field58::D(field));
170 }
171
172 Err(ParseError::InvalidFormat {
173 message: "Field 58 could not be parsed as either option A or D".to_string(),
174 })
175 }
176
177 fn parse_with_variant(
178 value: &str,
179 variant: Option<&str>,
180 _field_tag: Option<&str>,
181 ) -> crate::Result<Self>
182 where
183 Self: Sized,
184 {
185 match variant {
186 Some("A") => {
187 let field = Field58A::parse(value)?;
188 Ok(Field58::A(field))
189 }
190 Some("D") => {
191 let field = Field58D::parse(value)?;
192 Ok(Field58::D(field))
193 }
194 _ => {
195 Self::parse(value)
197 }
198 }
199 }
200
201 fn to_swift_string(&self) -> String {
202 match self {
203 Field58::A(field) => field.to_swift_string(),
204 Field58::D(field) => field.to_swift_string(),
205 }
206 }
207
208 fn get_variant_tag(&self) -> Option<&'static str> {
209 match self {
210 Field58::A(_) => Some("A"),
211 Field58::D(_) => Some("D"),
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn test_field58a_parse_with_bic_only() {
222 let field = Field58A::parse("DEUTDEFF").unwrap();
223 assert_eq!(field.party_identifier, None);
224 assert_eq!(field.bic, "DEUTDEFF");
225 }
226
227 #[test]
228 fn test_field58a_parse_with_party_identifier() {
229 let field = Field58A::parse("/CHGS123456\nDEUTDEFF").unwrap();
230 assert_eq!(field.party_identifier, Some("CHGS123456".to_string()));
231 assert_eq!(field.bic, "DEUTDEFF");
232 }
233
234 #[test]
235 fn test_field58a_to_swift_string() {
236 let field = Field58A {
237 party_identifier: Some("CHGS123456".to_string()),
238 bic: "DEUTDEFF".to_string(),
239 };
240 assert_eq!(field.to_swift_string(), ":58A:/CHGS123456\nDEUTDEFF");
241 }
242
243 #[test]
244 fn test_field58d_parse_with_name_only() {
245 let input = "DEUTSCHE BANK AG\nFRANKFURT AM MAIN\nGERMANY";
246 let field = Field58D::parse(input).unwrap();
247 assert_eq!(field.party_identifier, None);
248 assert_eq!(field.name_and_address.len(), 3);
249 assert_eq!(field.name_and_address[0], "DEUTSCHE BANK AG");
250 assert_eq!(field.name_and_address[1], "FRANKFURT AM MAIN");
251 assert_eq!(field.name_and_address[2], "GERMANY");
252 }
253
254 #[test]
255 fn test_field58d_parse_with_party_identifier() {
256 let input = "/CH123456\nDEUTSCHE BANK AG\nFRANKFURT AM MAIN";
257 let field = Field58D::parse(input).unwrap();
258 assert_eq!(field.party_identifier, Some("CH123456".to_string()));
259 assert_eq!(field.name_and_address.len(), 2);
260 assert_eq!(field.name_and_address[0], "DEUTSCHE BANK AG");
261 assert_eq!(field.name_and_address[1], "FRANKFURT AM MAIN");
262 }
263
264 #[test]
265 fn test_field58d_line_too_long() {
266 let input = "THIS BANK NAME IS MUCH TOO LONG TO BE ACCEPTED IN FIELD 58D";
267 assert!(Field58D::parse(input).is_err());
268 }
269
270 #[test]
271 fn test_field58_enum_to_swift_string() {
272 let field_a = Field58::A(Field58A {
273 party_identifier: None,
274 bic: "DEUTDEFF".to_string(),
275 });
276 assert_eq!(field_a.to_swift_string(), ":58A:DEUTDEFF");
277
278 let field_d = Field58::D(Field58D {
279 party_identifier: None,
280 name_and_address: vec!["DEUTSCHE BANK AG".to_string()],
281 });
282 assert_eq!(field_d.to_swift_string(), ":58D:DEUTSCHE BANK AG");
283 }
284}