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