1use crate::{SwiftField, ValidationResult};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
100pub struct Field53B {
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub account_line_indicator: Option<String>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub account_number: Option<String>,
107 pub party_identifier: String,
109}
110
111impl Field53B {
112 pub fn new(
114 account_line_indicator: Option<String>,
115 account_number: Option<String>,
116 party_identifier: impl Into<String>,
117 ) -> Result<Self, crate::ParseError> {
118 let party_identifier = party_identifier.into().trim().to_string();
119
120 if party_identifier.is_empty() {
122 return Err(crate::ParseError::InvalidFieldFormat {
123 field_tag: "53B".to_string(),
124 message: "Party identifier cannot be empty".to_string(),
125 });
126 }
127
128 if party_identifier.len() > 35 {
129 return Err(crate::ParseError::InvalidFieldFormat {
130 field_tag: "53B".to_string(),
131 message: "Party identifier cannot exceed 35 characters".to_string(),
132 });
133 }
134
135 if !party_identifier
136 .chars()
137 .all(|c| c.is_ascii() && !c.is_control())
138 {
139 return Err(crate::ParseError::InvalidFieldFormat {
140 field_tag: "53B".to_string(),
141 message: "Party identifier contains invalid characters".to_string(),
142 });
143 }
144
145 if let Some(ref indicator) = account_line_indicator {
147 if indicator.is_empty() {
148 return Err(crate::ParseError::InvalidFieldFormat {
149 field_tag: "53B".to_string(),
150 message: "Account line indicator cannot be empty if specified".to_string(),
151 });
152 }
153
154 if indicator.len() != 1 {
155 return Err(crate::ParseError::InvalidFieldFormat {
156 field_tag: "53B".to_string(),
157 message: "Account line indicator must be exactly 1 character".to_string(),
158 });
159 }
160
161 if !indicator.chars().all(|c| c.is_ascii() && !c.is_control()) {
162 return Err(crate::ParseError::InvalidFieldFormat {
163 field_tag: "53B".to_string(),
164 message: "Account line indicator contains invalid characters".to_string(),
165 });
166 }
167 }
168
169 if let Some(ref account) = account_number {
171 if account.is_empty() {
172 return Err(crate::ParseError::InvalidFieldFormat {
173 field_tag: "53B".to_string(),
174 message: "Account number cannot be empty if specified".to_string(),
175 });
176 }
177
178 if account.len() > 34 {
179 return Err(crate::ParseError::InvalidFieldFormat {
180 field_tag: "53B".to_string(),
181 message: "Account number cannot exceed 34 characters".to_string(),
182 });
183 }
184
185 if !account.chars().all(|c| c.is_ascii() && !c.is_control()) {
186 return Err(crate::ParseError::InvalidFieldFormat {
187 field_tag: "53B".to_string(),
188 message: "Account number contains invalid characters".to_string(),
189 });
190 }
191 }
192
193 Ok(Field53B {
194 account_line_indicator,
195 account_number,
196 party_identifier,
197 })
198 }
199
200 pub fn account_line_indicator(&self) -> Option<&str> {
202 self.account_line_indicator.as_deref()
203 }
204
205 pub fn account_number(&self) -> Option<&str> {
207 self.account_number.as_deref()
208 }
209
210 pub fn party_identifier(&self) -> &str {
212 &self.party_identifier
213 }
214
215 pub fn description(&self) -> String {
217 format!(
218 "Sender's Correspondent (Party ID: {})",
219 self.party_identifier
220 )
221 }
222}
223
224impl SwiftField for Field53B {
225 fn parse(content: &str) -> crate::Result<Self> {
226 let content = content.trim();
227 if content.is_empty() {
228 return Err(crate::ParseError::InvalidFieldFormat {
229 field_tag: "53B".to_string(),
230 message: "Field content cannot be empty".to_string(),
231 });
232 }
233
234 let content = if let Some(stripped) = content.strip_prefix(":53B:") {
235 stripped
236 } else if let Some(stripped) = content.strip_prefix("53B:") {
237 stripped
238 } else {
239 content
240 };
241
242 let mut account_line_indicator = None;
243 let mut account_number = None;
244 let mut party_identifier_content = content;
245
246 if content.starts_with('/') {
248 let lines: Vec<&str> = content.lines().collect();
249 if !lines.is_empty() {
250 let first_line = lines[0];
251
252 if first_line.len() == 2 && first_line.starts_with('/') {
253 account_line_indicator = Some(first_line[1..].to_string());
255 party_identifier_content = if lines.len() > 1 { lines[1] } else { "" };
256 } else if first_line.len() > 2 && first_line.starts_with('/') {
257 let parts: Vec<&str> = first_line[1..].split('/').collect();
259 if parts.len() == 2 {
260 account_line_indicator = Some(parts[0].to_string());
262 account_number = Some(parts[1].to_string());
263 } else {
264 account_number = Some(parts[0].to_string());
266 }
267 party_identifier_content = if lines.len() > 1 { lines[1] } else { "" };
268 }
269 }
270 }
271
272 let party_identifier = party_identifier_content.trim().to_string();
273 if party_identifier.is_empty() {
274 return Err(crate::ParseError::InvalidFieldFormat {
275 field_tag: "53B".to_string(),
276 message: "Party identifier is required".to_string(),
277 });
278 }
279
280 Field53B::new(account_line_indicator, account_number, party_identifier)
281 }
282
283 fn to_swift_string(&self) -> String {
284 let mut result = String::new();
285
286 if let Some(ref indicator) = self.account_line_indicator {
287 result.push('/');
288 result.push_str(indicator);
289 }
290
291 if let Some(ref account) = self.account_number {
292 result.push('/');
293 result.push_str(account);
294 }
295
296 if !result.is_empty() {
297 result.push('\n');
298 }
299 result.push_str(&self.party_identifier);
300
301 format!(":53B:{}", result)
302 }
303
304 fn validate(&self) -> ValidationResult {
305 ValidationResult {
307 is_valid: true,
308 errors: Vec::new(),
309 warnings: Vec::new(),
310 }
311 }
312
313 fn format_spec() -> &'static str {
314 "[/1!a][/34x]35x"
315 }
316}
317
318impl std::fmt::Display for Field53B {
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 match (&self.account_line_indicator, &self.account_number) {
321 (Some(indicator), Some(account)) => write!(
322 f,
323 "Indicator: {}, Account: {}, Party: {}",
324 indicator, account, self.party_identifier
325 ),
326 (None, Some(account)) => {
327 write!(f, "Account: {}, Party: {}", account, self.party_identifier)
328 }
329 (Some(indicator), None) => write!(
330 f,
331 "Indicator: {}, Party: {}",
332 indicator, self.party_identifier
333 ),
334 (None, None) => write!(f, "Party: {}", self.party_identifier),
335 }
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn test_field53b_creation_party_only() {
345 let field = Field53B::new(None, None, "PARTYID12345").unwrap();
346 assert_eq!(field.party_identifier(), "PARTYID12345");
347 assert!(field.account_number().is_none());
348 assert!(field.account_line_indicator().is_none());
349 }
350
351 #[test]
352 fn test_field53b_creation_with_account() {
353 let field = Field53B::new(None, Some("1234567890".to_string()), "PARTYID12345").unwrap();
354 assert_eq!(field.party_identifier(), "PARTYID12345");
355 assert_eq!(field.account_number(), Some("1234567890"));
356 assert!(field.account_line_indicator().is_none());
357 }
358
359 #[test]
360 fn test_field53b_creation_with_account_line_indicator() {
361 let field = Field53B::new(
362 Some("A".to_string()),
363 Some("1234567890".to_string()),
364 "PARTYID12345",
365 )
366 .unwrap();
367 assert_eq!(field.party_identifier(), "PARTYID12345");
368 assert_eq!(field.account_number(), Some("1234567890"));
369 assert_eq!(field.account_line_indicator(), Some("A"));
370 }
371
372 #[test]
373 fn test_field53b_parse_party_only() {
374 let field = Field53B::parse("PARTYID12345").unwrap();
375 assert_eq!(field.party_identifier(), "PARTYID12345");
376 assert!(field.account_number().is_none());
377 }
378
379 #[test]
380 fn test_field53b_parse_with_account() {
381 let field = Field53B::parse("/1234567890\nPARTYID12345").unwrap();
382 assert_eq!(field.party_identifier(), "PARTYID12345");
383 assert_eq!(field.account_number(), Some("1234567890"));
384 }
385
386 #[test]
387 fn test_field53b_parse_with_tag() {
388 let field = Field53B::parse(":53B:PARTYID12345").unwrap();
389 assert_eq!(field.party_identifier(), "PARTYID12345");
390 }
391
392 #[test]
393 fn test_field53b_to_swift_string() {
394 let field = Field53B::new(None, None, "PARTYID12345").unwrap();
395 assert_eq!(field.to_swift_string(), ":53B:PARTYID12345");
396
397 let field = Field53B::new(None, Some("1234567890".to_string()), "PARTYID12345").unwrap();
398 assert_eq!(field.to_swift_string(), ":53B:/1234567890\nPARTYID12345");
399 }
400
401 #[test]
402 fn test_field53b_display() {
403 let field = Field53B::new(None, None, "PARTYID12345").unwrap();
404 assert_eq!(format!("{}", field), "Party: PARTYID12345");
405
406 let field = Field53B::new(None, Some("1234567890".to_string()), "PARTYID12345").unwrap();
407 assert_eq!(
408 format!("{}", field),
409 "Account: 1234567890, Party: PARTYID12345"
410 );
411 }
412
413 #[test]
414 fn test_field53b_description() {
415 let field = Field53B::new(None, None, "PARTYID12345").unwrap();
416 assert_eq!(
417 field.description(),
418 "Sender's Correspondent (Party ID: PARTYID12345)"
419 );
420 }
421
422 #[test]
423 fn test_field53b_validation_empty_party() {
424 let result = Field53B::new(None, None, "");
425 assert!(result.is_err());
426 assert!(result.unwrap_err().to_string().contains("cannot be empty"));
427 }
428
429 #[test]
430 fn test_field53b_validation_party_too_long() {
431 let party = "A".repeat(36); let result = Field53B::new(None, None, party);
433 assert!(result.is_err());
434 assert!(
435 result
436 .unwrap_err()
437 .to_string()
438 .contains("cannot exceed 35 characters")
439 );
440 }
441
442 #[test]
443 fn test_field53b_validation_invalid_characters() {
444 let result = Field53B::new(None, None, "PARTY\x00ID"); 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_field53b_validation_account_too_long() {
456 let account = "A".repeat(35); let result = Field53B::new(None, Some(account), "PARTYID12345");
458 assert!(result.is_err());
459 assert!(
460 result
461 .unwrap_err()
462 .to_string()
463 .contains("cannot exceed 34 characters")
464 );
465 }
466
467 #[test]
468 fn test_field53b_validate() {
469 let field = Field53B::new(None, None, "PARTYID12345").unwrap();
470 let validation = field.validate();
471 assert!(validation.is_valid);
472 assert!(validation.errors.is_empty());
473 }
474}