1use serde::{Deserialize, Serialize};
59use std::collections::HashMap;
60use std::fmt::Debug;
61
62pub mod errors;
63pub mod fields;
64pub mod headers;
65pub mod messages;
66pub mod parser;
67
68pub use errors::{ParseError, Result, ValidationError};
70pub use headers::{ApplicationHeader, BasicHeader, Trailer, UserHeader};
71pub use parser::SwiftParser;
72
73pub use swift_mt_message_macros::{SwiftField, SwiftMessage, swift_serde};
75
76pub type SwiftResult<T> = std::result::Result<T, crate::errors::ParseError>;
78
79pub trait SwiftField: Serialize + for<'de> Deserialize<'de> + Clone + std::fmt::Debug {
81 fn parse(value: &str) -> Result<Self>
83 where
84 Self: Sized;
85
86 fn to_swift_string(&self) -> String;
88
89 fn validate(&self) -> ValidationResult;
91
92 fn format_spec() -> &'static str;
94}
95
96pub trait SwiftMessageBody: Debug + Clone + Send + Sync + Serialize {
98 fn message_type() -> &'static str;
100
101 fn from_fields(fields: HashMap<String, String>) -> SwiftResult<Self>
103 where
104 Self: Sized;
105
106 fn to_fields(&self) -> HashMap<String, String>;
108
109 fn required_fields() -> Vec<&'static str>;
111
112 fn optional_fields() -> Vec<&'static str>;
114}
115
116#[derive(Debug, Clone, Serialize)]
118pub struct SwiftMessage<T: SwiftMessageBody> {
119 pub basic_header: BasicHeader,
121
122 pub application_header: ApplicationHeader,
124
125 pub user_header: Option<UserHeader>,
127
128 pub trailer: Option<Trailer>,
130
131 pub blocks: RawBlocks,
133
134 pub message_type: String,
136
137 pub field_order: Vec<String>,
139
140 pub fields: T,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize, Default)]
146pub struct RawBlocks {
147 pub block1: Option<String>,
148 pub block2: Option<String>,
149 pub block3: Option<String>,
150 pub block4: String,
151 pub block5: Option<String>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct ValidationResult {
157 pub is_valid: bool,
158 pub errors: Vec<ValidationError>,
159 pub warnings: Vec<String>,
160}
161
162impl ValidationResult {
163 pub fn valid() -> Self {
164 Self {
165 is_valid: true,
166 errors: Vec::new(),
167 warnings: Vec::new(),
168 }
169 }
170
171 pub fn with_error(error: ValidationError) -> Self {
172 Self {
173 is_valid: false,
174 errors: vec![error],
175 warnings: Vec::new(),
176 }
177 }
178
179 pub fn with_errors(errors: Vec<ValidationError>) -> Self {
180 Self {
181 is_valid: errors.is_empty(),
182 errors,
183 warnings: Vec::new(),
184 }
185 }
186}
187
188pub mod common {
190 use serde::{Deserialize, Serialize};
191
192 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
194 pub struct BIC {
195 pub value: String,
196 }
197
198 impl BIC {
199 pub fn new(value: String) -> Self {
200 Self { value }
201 }
202
203 pub fn validate(&self) -> bool {
204 let len = self.value.len();
206 (len == 8 || len == 11) && self.value.chars().all(|c| c.is_alphanumeric())
207 }
208 }
209
210 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
212 pub struct Currency {
213 pub code: String,
214 }
215
216 impl Currency {
217 pub fn new(code: String) -> Self {
218 Self {
219 code: code.to_uppercase(),
220 }
221 }
222 }
223
224 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
226 pub struct Amount {
227 pub value: String,
228 pub decimal_places: u8,
229 }
230
231 impl Amount {
232 pub fn new(value: String) -> Self {
233 let decimal_places = if value.contains(',') {
234 value.split(',').nth(1).map(|s| s.len() as u8).unwrap_or(0)
235 } else {
236 0
237 };
238
239 Self {
240 value,
241 decimal_places,
242 }
243 }
244
245 pub fn to_decimal(&self) -> Result<f64, std::num::ParseFloatError> {
246 self.value.replace(',', ".").parse()
247 }
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use crate::messages::mt103::MT103;
255
256 #[test]
257 fn test_full_mt103_parsing() {
258 let raw_message = r#"{1:F01BNPAFRPPXXX0000000000}{2:O1031234240101DEUTDEFFXXXX12345678952401011234N}{3:{103:EBA}}{4:
259:20:FT21001234567890
260:23B:CRED
261:32A:240101USD1000,00
262:50K:/1234567890
263ACME CORPORATION
264123 MAIN STREET
265NEW YORK NY 10001
266:52A:BNPAFRPPXXX
267:57A:DEUTDEFFXXX
268:59:/DE89370400440532013000
269MUELLER GMBH
270HAUPTSTRASSE 1
27110115 BERLIN
272:70:PAYMENT FOR INVOICE 12345
273:71A:OUR
274-}"#;
275
276 let result = SwiftParser::parse::<MT103>(raw_message);
277 assert!(result.is_ok(), "Parsing should succeed: {:?}", result.err());
278
279 let parsed = result.unwrap();
280 assert_eq!(parsed.message_type, "103");
281
282 let json = serde_json::to_string_pretty(&parsed);
284 assert!(json.is_ok(), "JSON serialization should work");
285 println!("Parsed MT103 JSON:\n{}", json.unwrap());
286 }
287
288 #[test]
289 fn test_field_parsing() {
290 use crate::fields::field20::Field20;
291
292 let result = Field20::parse(":20:FT21001234567890");
293 assert!(result.is_ok());
294
295 let field = result.unwrap();
296 assert_eq!(field.to_swift_string(), ":20:FT21001234567890");
297 }
298}