swift_mt_message/fields/
field52a.rs1use crate::common::BIC;
2use crate::{SwiftField, ValidationError, ValidationResult};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
91pub struct Field52A {
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub account_line_indicator: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub account_number: Option<String>,
98 #[serde(flatten)]
100 pub bic: BIC,
101}
102
103impl Field52A {
104 pub fn new(
106 account_line_indicator: Option<String>,
107 account_number: Option<String>,
108 bic: impl Into<String>,
109 ) -> Result<Self, crate::ParseError> {
110 let bic = BIC::parse(&bic.into(), Some("52A"))?;
112
113 if let Some(ref indicator) = account_line_indicator {
115 if indicator.is_empty() {
116 return Err(crate::ParseError::InvalidFieldFormat {
117 field_tag: "52A".to_string(),
118 message: "Account line indicator cannot be empty".to_string(),
119 });
120 }
121 if indicator.len() != 1 {
122 return Err(crate::ParseError::InvalidFieldFormat {
123 field_tag: "52A".to_string(),
124 message: "Account line indicator must be exactly 1 character".to_string(),
125 });
126 }
127 if !indicator.chars().all(|c| c.is_ascii() && !c.is_control()) {
128 return Err(crate::ParseError::InvalidFieldFormat {
129 field_tag: "52A".to_string(),
130 message: "Account line indicator contains invalid characters".to_string(),
131 });
132 }
133 }
134
135 if let Some(ref account) = account_number {
137 if account.is_empty() {
138 return Err(crate::ParseError::InvalidFieldFormat {
139 field_tag: "52A".to_string(),
140 message: "Account number cannot be empty".to_string(),
141 });
142 }
143 if account.len() > 34 {
144 return Err(crate::ParseError::InvalidFieldFormat {
145 field_tag: "52A".to_string(),
146 message: "Account number cannot exceed 34 characters".to_string(),
147 });
148 }
149 if !account.chars().all(|c| c.is_ascii() && !c.is_control()) {
150 return Err(crate::ParseError::InvalidFieldFormat {
151 field_tag: "52A".to_string(),
152 message: "Account number contains invalid characters".to_string(),
153 });
154 }
155 }
156
157 Ok(Field52A {
158 account_line_indicator,
159 account_number,
160 bic,
161 })
162 }
163
164 pub fn account_line_indicator(&self) -> Option<&str> {
166 self.account_line_indicator.as_deref()
167 }
168
169 pub fn account_number(&self) -> Option<&str> {
171 self.account_number.as_deref()
172 }
173
174 pub fn bic(&self) -> &str {
176 self.bic.value()
177 }
178
179 pub fn is_full_bic(&self) -> bool {
181 self.bic.is_full_bic()
182 }
183}
184
185impl SwiftField for Field52A {
186 fn parse(content: &str) -> crate::Result<Self> {
187 let content = content.trim();
188 if content.is_empty() {
189 return Err(crate::ParseError::InvalidFieldFormat {
190 field_tag: "52A".to_string(),
191 message: "Field content cannot be empty".to_string(),
192 });
193 }
194
195 let content = if let Some(stripped) = content.strip_prefix(":52A:") {
196 stripped } else if let Some(stripped) = content.strip_prefix("52A:") {
198 stripped } else {
200 content
201 };
202
203 let mut account_line_indicator = None;
204 let mut account_number = None;
205 let mut bic_content = content;
206
207 if content.starts_with('/') {
209 let lines: Vec<&str> = content.lines().collect();
210 if !lines.is_empty() {
211 let first_line = lines[0];
212
213 if first_line.len() == 2 && first_line.starts_with('/') {
214 account_line_indicator = Some(first_line[1..].to_string());
216 bic_content = if lines.len() > 1 { lines[1] } else { "" };
217 } else if first_line.len() > 2 && first_line.starts_with('/') {
218 let parts: Vec<&str> = first_line[1..].split('/').collect();
220 if parts.len() == 2 {
221 account_line_indicator = Some(parts[0].to_string());
223 account_number = Some(parts[1].to_string());
224 } else {
225 account_number = Some(parts[0].to_string());
227 }
228 bic_content = if lines.len() > 1 { lines[1] } else { "" };
229 }
230 }
231 }
232
233 let bic_str = bic_content.trim();
234 if bic_str.is_empty() {
235 return Err(crate::ParseError::InvalidFieldFormat {
236 field_tag: "52A".to_string(),
237 message: "BIC code is required".to_string(),
238 });
239 }
240
241 let bic = BIC::parse(bic_str, Some("52A"))?;
242
243 Ok(Field52A {
244 account_line_indicator,
245 account_number,
246 bic,
247 })
248 }
249
250 fn to_swift_string(&self) -> String {
251 let mut result = String::new();
252
253 if let Some(ref indicator) = self.account_line_indicator {
254 result.push('/');
255 result.push_str(indicator);
256 }
257
258 if let Some(ref account) = self.account_number {
259 result.push('/');
260 result.push_str(account);
261 }
262
263 if !result.is_empty() {
264 result.push('\n');
265 }
266 result.push_str(self.bic.value());
267
268 format!(":52A:{}", result)
269 }
270
271 fn validate(&self) -> ValidationResult {
272 let mut errors = Vec::new();
273
274 let bic_validation = self.bic.validate();
276 if !bic_validation.is_valid {
277 errors.extend(bic_validation.errors);
278 }
279
280 if let Some(indicator) = &self.account_line_indicator {
282 if indicator.len() != 1 {
283 errors.push(ValidationError::LengthValidation {
284 field_tag: "52A".to_string(),
285 expected: "1 character".to_string(),
286 actual: indicator.len(),
287 });
288 }
289 }
290
291 if let Some(account) = &self.account_number {
292 if account.len() > 34 {
293 errors.push(ValidationError::LengthValidation {
294 field_tag: "52A".to_string(),
295 expected: "max 34 characters".to_string(),
296 actual: account.len(),
297 });
298 }
299 }
300
301 ValidationResult {
302 is_valid: errors.is_empty(),
303 errors,
304 warnings: Vec::new(),
305 }
306 }
307
308 fn format_spec() -> &'static str {
309 "[/1!a][/34x]4!a2!a2!c[3!c]"
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_field52a_creation_bic_only() {
319 let field = Field52A::new(None, None, "BNPAFRPPXXX").unwrap();
320 assert_eq!(field.bic(), "BNPAFRPPXXX");
321 assert!(field.account_number().is_none());
322 assert!(field.account_line_indicator().is_none());
323 assert!(field.is_full_bic());
324 }
325
326 #[test]
327 fn test_field52a_creation_with_account() {
328 let field = Field52A::new(None, Some("1234567890".to_string()), "BNPAFRPPXXX").unwrap();
329 assert_eq!(field.bic(), "BNPAFRPPXXX");
330 assert_eq!(field.account_number(), Some("1234567890"));
331 assert!(field.account_line_indicator().is_none());
332 }
333
334 #[test]
335 fn test_field52a_creation_with_account_line_indicator() {
336 let field = Field52A::new(
337 Some("A".to_string()),
338 Some("1234567890".to_string()),
339 "BNPAFRPPXXX",
340 )
341 .unwrap();
342 assert_eq!(field.bic(), "BNPAFRPPXXX");
343 assert_eq!(field.account_number(), Some("1234567890"));
344 assert_eq!(field.account_line_indicator(), Some("A"));
345 }
346
347 #[test]
348 fn test_field52a_parse_bic_only() {
349 let field = Field52A::parse("BNPAFRPPXXX").unwrap();
350 assert_eq!(field.bic(), "BNPAFRPPXXX");
351 assert!(field.account_number().is_none());
352 }
353
354 #[test]
355 fn test_field52a_parse_with_account() {
356 let field = Field52A::parse("/1234567890\nBNPAFRPPXXX").unwrap();
357 assert_eq!(field.bic(), "BNPAFRPPXXX");
358 assert_eq!(field.account_number(), Some("1234567890"));
359 }
360
361 #[test]
362 fn test_field52a_to_swift_string() {
363 let field = Field52A::new(
364 Some("A".to_string()),
365 Some("1234567890".to_string()),
366 "BNPAFRPPXXX",
367 )
368 .unwrap();
369 assert_eq!(field.to_swift_string(), ":52A:/A/1234567890\nBNPAFRPPXXX");
370 }
371}