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