1use crate::error::{MTError, Result};
4use crate::messages::MTMessage;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ValidationLevel {
9 Basic,
11 Standard,
13 Strict,
15}
16
17#[derive(Debug, Clone)]
19pub struct ValidationResult {
20 pub errors: Vec<ValidationError>,
21 pub warnings: Vec<ValidationWarning>,
22}
23
24impl ValidationResult {
25 pub fn new() -> Self {
26 Self {
27 errors: Vec::new(),
28 warnings: Vec::new(),
29 }
30 }
31
32 pub fn is_valid(&self) -> bool {
33 self.errors.is_empty()
34 }
35
36 pub fn has_warnings(&self) -> bool {
37 !self.warnings.is_empty()
38 }
39
40 pub fn add_error(&mut self, error: ValidationError) {
41 self.errors.push(error);
42 }
43
44 pub fn add_warning(&mut self, warning: ValidationWarning) {
45 self.warnings.push(warning);
46 }
47
48 pub fn errors(&self) -> &[ValidationError] {
49 &self.errors
50 }
51
52 pub fn warnings(&self) -> &[ValidationWarning] {
53 &self.warnings
54 }
55}
56
57impl Default for ValidationResult {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct ValidationError {
66 pub field: Option<String>,
67 pub message: String,
68 pub error_code: String,
69}
70
71impl ValidationError {
72 pub fn new(field: Option<String>, message: String, error_code: String) -> Self {
73 Self {
74 field,
75 message,
76 error_code,
77 }
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct ValidationWarning {
84 pub field: Option<String>,
85 pub message: String,
86 pub warning_code: String,
87}
88
89impl ValidationWarning {
90 pub fn new(field: Option<String>, message: String, warning_code: String) -> Self {
91 Self {
92 field,
93 message,
94 warning_code,
95 }
96 }
97}
98
99pub struct MTValidator;
101
102impl MTValidator {
103 pub fn validate(message: &MTMessage, level: ValidationLevel) -> ValidationResult {
105 let mut result = ValidationResult::new();
106
107 match message {
108 MTMessage::MT103(mt103) => {
109 Self::validate_mt103(mt103, level, &mut result);
110 }
111 MTMessage::MT102(mt102) => {
112 Self::validate_mt102(mt102, level, &mut result);
113 }
114 MTMessage::MT202(mt202) => {
115 Self::validate_mt202(mt202, level, &mut result);
116 }
117 MTMessage::MT940(mt940) => {
118 Self::validate_mt940(mt940, level, &mut result);
119 }
120 MTMessage::MT941(mt941) => {
121 Self::validate_mt941(mt941, level, &mut result);
122 }
123 MTMessage::MT942(mt942) => {
124 Self::validate_mt942(mt942, level, &mut result);
125 }
126 MTMessage::MT192(mt192) => {
127 Self::validate_mt192(mt192, level, &mut result);
128 }
129 MTMessage::MT195(mt195) => {
130 Self::validate_mt195(mt195, level, &mut result);
131 }
132 MTMessage::MT196(mt196) => {
133 Self::validate_mt196(mt196, level, &mut result);
134 }
135 MTMessage::MT197(mt197) => {
136 Self::validate_mt197(mt197, level, &mut result);
137 }
138 MTMessage::MT199(mt199) => {
139 Self::validate_mt199(mt199, level, &mut result);
140 }
141 }
142
143 result
144 }
145
146 fn validate_mt103(
147 _mt103: &crate::messages::mt103::MT103,
148 level: ValidationLevel,
149 _result: &mut ValidationResult,
150 ) {
151 match level {
152 ValidationLevel::Basic => {
153 }
156 ValidationLevel::Standard => {
157 }
159 ValidationLevel::Strict => {
160 }
162 }
163 }
164
165 fn validate_mt102(
166 _mt102: &crate::messages::mt102::MT102,
167 _level: ValidationLevel,
168 _result: &mut ValidationResult,
169 ) {
170 }
172
173 fn validate_mt202(
174 _mt202: &crate::messages::mt202::MT202,
175 _level: ValidationLevel,
176 _result: &mut ValidationResult,
177 ) {
178 }
180
181 fn validate_mt940(
182 _mt940: &crate::messages::mt940::MT940,
183 _level: ValidationLevel,
184 _result: &mut ValidationResult,
185 ) {
186 }
188
189 fn validate_mt941(
190 _mt941: &crate::messages::mt941::MT941,
191 _level: ValidationLevel,
192 _result: &mut ValidationResult,
193 ) {
194 }
196
197 fn validate_mt942(
198 _mt942: &crate::messages::mt942::MT942,
199 _level: ValidationLevel,
200 _result: &mut ValidationResult,
201 ) {
202 }
204
205 fn validate_mt192(
206 _mt192: &crate::messages::mt192::MT192,
207 _level: ValidationLevel,
208 _result: &mut ValidationResult,
209 ) {
210 }
212
213 fn validate_mt195(
214 _mt195: &crate::messages::mt195::MT195,
215 _level: ValidationLevel,
216 _result: &mut ValidationResult,
217 ) {
218 }
220
221 fn validate_mt196(
222 _mt196: &crate::messages::mt196::MT196,
223 _level: ValidationLevel,
224 _result: &mut ValidationResult,
225 ) {
226 }
228
229 fn validate_mt197(
230 _mt197: &crate::messages::mt197::MT197,
231 _level: ValidationLevel,
232 _result: &mut ValidationResult,
233 ) {
234 }
236
237 fn validate_mt199(
238 _mt199: &crate::messages::mt199::MT199,
239 _level: ValidationLevel,
240 _result: &mut ValidationResult,
241 ) {
242 }
244}
245
246pub fn validate_message(message: &MTMessage, level: ValidationLevel) -> ValidationResult {
248 MTValidator::validate(message, level)
249}
250
251pub mod field_validators {
253 use super::*;
254 use regex::Regex;
255
256 pub fn validate_bic(bic: &str) -> Result<()> {
258 let bic_regex = Regex::new(r"^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$").unwrap();
259 if bic_regex.is_match(bic) {
260 Ok(())
261 } else {
262 Err(MTError::ValidationError {
263 field: "BIC".to_string(),
264 message: format!("Invalid BIC format: {}", bic),
265 })
266 }
267 }
268
269 pub fn validate_iban(iban: &str) -> Result<()> {
271 let iban_regex = Regex::new(r"^[A-Z]{2}[0-9]{2}[A-Z0-9]{4}[0-9]{7}([A-Z0-9]?){0,16}$").unwrap();
273 if iban_regex.is_match(iban) {
274 Ok(())
275 } else {
276 Err(MTError::ValidationError {
277 field: "IBAN".to_string(),
278 message: format!("Invalid IBAN format: {}", iban),
279 })
280 }
281 }
282
283 pub fn validate_amount(amount: &str) -> Result<()> {
285 let amount_regex = Regex::new(r"^[A-Z]{3}[0-9]+([,.][0-9]{1,2})?$").unwrap();
286 if amount_regex.is_match(amount) {
287 Ok(())
288 } else {
289 Err(MTError::ValidationError {
290 field: "Amount".to_string(),
291 message: format!("Invalid amount format: {}", amount),
292 })
293 }
294 }
295
296 pub fn validate_date_yymmdd(date: &str) -> Result<()> {
298 use crate::common::SwiftDate;
299
300 let date_regex = Regex::new(r"^[0-9]{6}$").unwrap();
301 if date_regex.is_match(date) {
302 SwiftDate::parse_yymmdd(date).map_err(|_| MTError::ValidationError {
304 field: "Date".to_string(),
305 message: format!("Invalid date: {}", date),
306 })?;
307 Ok(())
308 } else {
309 Err(MTError::ValidationError {
310 field: "Date".to_string(),
311 message: format!("Invalid date format, expected YYMMDD: {}", date),
312 })
313 }
314 }
315
316 pub fn validate_reference(reference: &str) -> Result<()> {
318 if reference.len() > 16 {
319 return Err(MTError::ValidationError {
320 field: "Reference".to_string(),
321 message: format!("Reference too long (max 16 chars): {}", reference),
322 });
323 }
324
325 let ref_regex = Regex::new(r"^[A-Z0-9/\-?:().,'+\s]*$").unwrap();
326 if ref_regex.is_match(reference) {
327 Ok(())
328 } else {
329 Err(MTError::ValidationError {
330 field: "Reference".to_string(),
331 message: format!("Invalid reference format: {}", reference),
332 })
333 }
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use super::field_validators::*;
341
342 #[test]
343 fn test_validation_result() {
344 let mut result = ValidationResult::new();
345 assert!(result.is_valid());
346 assert!(!result.has_warnings());
347
348 result.add_error(ValidationError::new(
349 Some("20".to_string()),
350 "Missing field".to_string(),
351 "E001".to_string(),
352 ));
353 assert!(!result.is_valid());
354
355 result.add_warning(ValidationWarning::new(
356 Some("23B".to_string()),
357 "Deprecated field".to_string(),
358 "W001".to_string(),
359 ));
360 assert!(result.has_warnings());
361 }
362
363 #[test]
364 fn test_bic_validation() {
365 assert!(validate_bic("BANKDEFFXXX").is_ok());
366 assert!(validate_bic("BANKDEFF").is_ok());
367 assert!(validate_bic("BANKDE").is_err());
368 assert!(validate_bic("bankdeffxxx").is_err());
369 }
370
371 #[test]
372 fn test_amount_validation() {
373 assert!(validate_amount("EUR1234567,89").is_ok());
374 assert!(validate_amount("USD1000.50").is_ok());
375 assert!(validate_amount("EUR1000").is_ok());
376 assert!(validate_amount("1000.50").is_err());
377 assert!(validate_amount("EUR").is_err());
378 }
379
380 #[test]
381 fn test_date_validation() {
382 assert!(validate_date_yymmdd("210315").is_ok());
383 assert!(validate_date_yymmdd("991231").is_ok());
384 assert!(validate_date_yymmdd("210230").is_err()); assert!(validate_date_yymmdd("211301").is_err()); assert!(validate_date_yymmdd("21031").is_err()); }
388
389 #[test]
390 fn test_reference_validation() {
391 assert!(validate_reference("FT21234567890").is_ok());
392 assert!(validate_reference("REF-123/456").is_ok());
393 assert!(validate_reference("A".repeat(17).as_str()).is_err()); }
395}