1use crate::{SwiftField, ValidationResult};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
113pub struct Field57D {
114 pub lines: Vec<String>,
116}
117
118impl Field57D {
119 pub fn new(lines: Vec<String>) -> Result<Self, crate::ParseError> {
121 if lines.is_empty() {
122 return Err(crate::ParseError::InvalidFieldFormat {
123 field_tag: "57D".to_string(),
124 message: "At least one line is required".to_string(),
125 });
126 }
127
128 if lines.len() > 4 {
129 return Err(crate::ParseError::InvalidFieldFormat {
130 field_tag: "57D".to_string(),
131 message: "Cannot exceed 4 lines".to_string(),
132 });
133 }
134
135 for (i, line) in lines.iter().enumerate() {
136 let trimmed = line.trim();
137 if trimmed.is_empty() {
138 return Err(crate::ParseError::InvalidFieldFormat {
139 field_tag: "57D".to_string(),
140 message: format!("Line {} cannot be empty", i + 1),
141 });
142 }
143
144 if trimmed.len() > 35 {
145 return Err(crate::ParseError::InvalidFieldFormat {
146 field_tag: "57D".to_string(),
147 message: format!("Line {} cannot exceed 35 characters", i + 1),
148 });
149 }
150
151 if !trimmed.chars().all(|c| c.is_ascii() && !c.is_control()) {
152 return Err(crate::ParseError::InvalidFieldFormat {
153 field_tag: "57D".to_string(),
154 message: format!("Line {} contains invalid characters", i + 1),
155 });
156 }
157 }
158
159 let trimmed_lines: Vec<String> = lines.iter().map(|line| line.trim().to_string()).collect();
160
161 Ok(Field57D {
162 lines: trimmed_lines,
163 })
164 }
165
166 pub fn from_string(content: impl Into<String>) -> Result<Self, crate::ParseError> {
168 let content = content.into().trim().to_string();
169 if content.is_empty() {
170 return Err(crate::ParseError::InvalidFieldFormat {
171 field_tag: "57D".to_string(),
172 message: "Content cannot be empty".to_string(),
173 });
174 }
175 Self::new(vec![content])
176 }
177
178 pub fn add_line(&mut self, line: impl Into<String>) -> Result<(), crate::ParseError> {
180 if self.lines.len() >= 4 {
181 return Err(crate::ParseError::InvalidFieldFormat {
182 field_tag: "57D".to_string(),
183 message: "Cannot exceed 4 lines".to_string(),
184 });
185 }
186
187 let line = line.into().trim().to_string();
188 if line.is_empty() {
189 return Err(crate::ParseError::InvalidFieldFormat {
190 field_tag: "57D".to_string(),
191 message: "Line cannot be empty".to_string(),
192 });
193 }
194
195 if line.len() > 35 {
196 return Err(crate::ParseError::InvalidFieldFormat {
197 field_tag: "57D".to_string(),
198 message: "Line cannot exceed 35 characters".to_string(),
199 });
200 }
201
202 if !line.chars().all(|c| c.is_ascii() && !c.is_control()) {
203 return Err(crate::ParseError::InvalidFieldFormat {
204 field_tag: "57D".to_string(),
205 message: "Line contains invalid characters".to_string(),
206 });
207 }
208
209 self.lines.push(line);
210 Ok(())
211 }
212
213 pub fn line(&self, index: usize) -> Option<&str> {
215 self.lines.get(index).map(|s| s.as_str())
216 }
217
218 pub fn line_count(&self) -> usize {
220 self.lines.len()
221 }
222
223 pub fn lines(&self) -> &[String] {
225 &self.lines
226 }
227
228 pub fn description(&self) -> String {
230 format!(
231 "Account With Institution (Name/Address: {})",
232 self.lines.join(", ")
233 )
234 }
235}
236
237impl SwiftField for Field57D {
238 fn parse(content: &str) -> crate::Result<Self> {
239 let content = content.trim();
240 if content.is_empty() {
241 return Err(crate::ParseError::InvalidFieldFormat {
242 field_tag: "57D".to_string(),
243 message: "Field content cannot be empty".to_string(),
244 });
245 }
246
247 let content = if let Some(stripped) = content.strip_prefix(":57D:") {
248 stripped
249 } else if let Some(stripped) = content.strip_prefix("57D:") {
250 stripped
251 } else {
252 content
253 };
254
255 let lines: Vec<String> = content
256 .lines()
257 .map(|line| line.trim().to_string())
258 .filter(|line| !line.is_empty())
259 .collect();
260
261 Field57D::new(lines)
262 }
263
264 fn to_swift_string(&self) -> String {
265 format!(":57D:{}", self.lines.join("\n"))
266 }
267
268 fn validate(&self) -> ValidationResult {
269 ValidationResult {
271 is_valid: true,
272 errors: Vec::new(),
273 warnings: Vec::new(),
274 }
275 }
276
277 fn format_spec() -> &'static str {
278 "4*35x"
279 }
280}
281
282impl std::fmt::Display for Field57D {
283 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284 write!(f, "{}", self.lines.join(" / "))
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn test_field57d_creation_single_line() {
294 let field = Field57D::new(vec!["ACCOUNT WITH BANK NAME".to_string()]).unwrap();
295 assert_eq!(field.line_count(), 1);
296 assert_eq!(field.line(0), Some("ACCOUNT WITH BANK NAME"));
297 }
298
299 #[test]
300 fn test_field57d_creation_multiple_lines() {
301 let field = Field57D::new(vec![
302 "ACCOUNT WITH BANK NAME".to_string(),
303 "123 MAIN STREET".to_string(),
304 "NEW YORK, NY 10001".to_string(),
305 ])
306 .unwrap();
307 assert_eq!(field.line_count(), 3);
308 assert_eq!(field.line(0), Some("ACCOUNT WITH BANK NAME"));
309 assert_eq!(field.line(1), Some("123 MAIN STREET"));
310 assert_eq!(field.line(2), Some("NEW YORK, NY 10001"));
311 }
312
313 #[test]
314 fn test_field57d_from_string() {
315 let field = Field57D::from_string("ACCOUNT WITH BANK NAME").unwrap();
316 assert_eq!(field.line_count(), 1);
317 assert_eq!(field.line(0), Some("ACCOUNT WITH BANK NAME"));
318 }
319
320 #[test]
321 fn test_field57d_add_line() {
322 let mut field = Field57D::from_string("ACCOUNT WITH BANK NAME").unwrap();
323 field.add_line("123 MAIN STREET").unwrap();
324 assert_eq!(field.line_count(), 2);
325 assert_eq!(field.line(1), Some("123 MAIN STREET"));
326 }
327
328 #[test]
329 fn test_field57d_parse_single_line() {
330 let field = Field57D::parse("ACCOUNT WITH BANK NAME").unwrap();
331 assert_eq!(field.line_count(), 1);
332 assert_eq!(field.line(0), Some("ACCOUNT WITH BANK NAME"));
333 }
334
335 #[test]
336 fn test_field57d_parse_multiple_lines() {
337 let content = "ACCOUNT WITH BANK NAME\n123 MAIN STREET\nNEW YORK, NY 10001";
338 let field = Field57D::parse(content).unwrap();
339 assert_eq!(field.line_count(), 3);
340 assert_eq!(field.line(0), Some("ACCOUNT WITH BANK NAME"));
341 assert_eq!(field.line(1), Some("123 MAIN STREET"));
342 assert_eq!(field.line(2), Some("NEW YORK, NY 10001"));
343 }
344
345 #[test]
346 fn test_field57d_parse_with_tag() {
347 let field = Field57D::parse(":57D:ACCOUNT WITH BANK NAME").unwrap();
348 assert_eq!(field.line_count(), 1);
349 assert_eq!(field.line(0), Some("ACCOUNT WITH BANK NAME"));
350 }
351
352 #[test]
353 fn test_field57d_to_swift_string() {
354 let field = Field57D::new(vec![
355 "ACCOUNT WITH BANK NAME".to_string(),
356 "123 MAIN STREET".to_string(),
357 ])
358 .unwrap();
359 assert_eq!(
360 field.to_swift_string(),
361 ":57D:ACCOUNT WITH BANK NAME\n123 MAIN STREET"
362 );
363 }
364
365 #[test]
366 fn test_field57d_display() {
367 let field = Field57D::new(vec![
368 "ACCOUNT WITH BANK NAME".to_string(),
369 "123 MAIN STREET".to_string(),
370 ])
371 .unwrap();
372 assert_eq!(
373 format!("{}", field),
374 "ACCOUNT WITH BANK NAME / 123 MAIN STREET"
375 );
376 }
377
378 #[test]
379 fn test_field57d_description() {
380 let field = Field57D::from_string("ACCOUNT WITH BANK NAME").unwrap();
381 assert_eq!(
382 field.description(),
383 "Account With Institution (Name/Address: ACCOUNT WITH BANK NAME)"
384 );
385 }
386
387 #[test]
388 fn test_field57d_validation_empty_lines() {
389 let result = Field57D::new(vec![]);
390 assert!(result.is_err());
391 assert!(
392 result
393 .unwrap_err()
394 .to_string()
395 .contains("At least one line is required")
396 );
397 }
398
399 #[test]
400 fn test_field57d_validation_too_many_lines() {
401 let result = Field57D::new(vec![
402 "LINE1".to_string(),
403 "LINE2".to_string(),
404 "LINE3".to_string(),
405 "LINE4".to_string(),
406 "LINE5".to_string(),
407 ]);
408 assert!(result.is_err());
409 assert!(
410 result
411 .unwrap_err()
412 .to_string()
413 .contains("Cannot exceed 4 lines")
414 );
415 }
416
417 #[test]
418 fn test_field57d_validation_line_too_long() {
419 let long_line = "A".repeat(36); let result = Field57D::new(vec![long_line]);
421 assert!(result.is_err());
422 assert!(
423 result
424 .unwrap_err()
425 .to_string()
426 .contains("cannot exceed 35 characters")
427 );
428 }
429
430 #[test]
431 fn test_field57d_validation_empty_line() {
432 let result = Field57D::new(vec!["".to_string()]);
433 assert!(result.is_err());
434 assert!(result.unwrap_err().to_string().contains("cannot be empty"));
435 }
436
437 #[test]
438 fn test_field57d_validation_invalid_characters() {
439 let result = Field57D::new(vec!["BANK\x00NAME".to_string()]); assert!(result.is_err());
441 assert!(
442 result
443 .unwrap_err()
444 .to_string()
445 .contains("invalid characters")
446 );
447 }
448
449 #[test]
450 fn test_field57d_add_line_too_many() {
451 let mut field = Field57D::new(vec![
452 "LINE1".to_string(),
453 "LINE2".to_string(),
454 "LINE3".to_string(),
455 "LINE4".to_string(),
456 ])
457 .unwrap();
458 let result = field.add_line("LINE5");
459 assert!(result.is_err());
460 assert!(
461 result
462 .unwrap_err()
463 .to_string()
464 .contains("Cannot exceed 4 lines")
465 );
466 }
467
468 #[test]
469 fn test_field57d_validate() {
470 let field = Field57D::from_string("ACCOUNT WITH BANK NAME").unwrap();
471 let validation = field.validate();
472 assert!(validation.is_valid);
473 assert!(validation.errors.is_empty());
474 }
475}