swift_mt_message/fields/
field21.rs1use super::swift_utils::{parse_max_length, parse_swift_chars};
2use crate::errors::ParseError;
3use crate::traits::SwiftField;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct Field21NoOption {
19 pub reference: String,
21}
22
23impl SwiftField for Field21NoOption {
24 fn parse(input: &str) -> crate::Result<Self>
25 where
26 Self: Sized,
27 {
28 let reference = parse_max_length(input, 16, "Field 21 reference")?;
30
31 parse_swift_chars(&reference, "Field 21 reference")?;
33
34 if reference.starts_with('/') || reference.ends_with('/') {
36 return Err(ParseError::InvalidFormat {
37 message: "Field 21 reference cannot start or end with '/'".to_string(),
38 });
39 }
40
41 if reference.contains("//") {
43 return Err(ParseError::InvalidFormat {
44 message: "Field 21 reference cannot contain consecutive slashes '//'".to_string(),
45 });
46 }
47
48 Ok(Field21NoOption { reference })
49 }
50
51 fn to_swift_string(&self) -> String {
52 format!(":21:{}", self.reference)
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
62pub struct Field21C {
63 pub reference: String,
65}
66
67impl SwiftField for Field21C {
68 fn parse(input: &str) -> crate::Result<Self>
69 where
70 Self: Sized,
71 {
72 let reference = parse_max_length(input, 35, "Field 21C reference")?;
73 parse_swift_chars(&reference, "Field 21C reference")?;
74
75 if reference.starts_with('/') || reference.ends_with('/') {
76 return Err(ParseError::InvalidFormat {
77 message: "Field 21C reference cannot start or end with '/'".to_string(),
78 });
79 }
80
81 if reference.contains("//") {
82 return Err(ParseError::InvalidFormat {
83 message: "Field 21C reference cannot contain consecutive slashes '//'".to_string(),
84 });
85 }
86
87 Ok(Field21C { reference })
88 }
89
90 fn to_swift_string(&self) -> String {
91 format!(":21C:{}", self.reference)
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub struct Field21D {
102 pub reference: String,
104}
105
106impl SwiftField for Field21D {
107 fn parse(input: &str) -> crate::Result<Self>
108 where
109 Self: Sized,
110 {
111 let reference = parse_max_length(input, 35, "Field 21D reference")?;
112 parse_swift_chars(&reference, "Field 21D reference")?;
113
114 if reference.starts_with('/') || reference.ends_with('/') {
115 return Err(ParseError::InvalidFormat {
116 message: "Field 21D reference cannot start or end with '/'".to_string(),
117 });
118 }
119
120 if reference.contains("//") {
121 return Err(ParseError::InvalidFormat {
122 message: "Field 21D reference cannot contain consecutive slashes '//'".to_string(),
123 });
124 }
125
126 Ok(Field21D { reference })
127 }
128
129 fn to_swift_string(&self) -> String {
130 format!(":21D:{}", self.reference)
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
140pub struct Field21E {
141 pub reference: String,
143}
144
145impl SwiftField for Field21E {
146 fn parse(input: &str) -> crate::Result<Self>
147 where
148 Self: Sized,
149 {
150 let reference = parse_max_length(input, 35, "Field 21E reference")?;
151 parse_swift_chars(&reference, "Field 21E reference")?;
152
153 if reference.starts_with('/') || reference.ends_with('/') {
154 return Err(ParseError::InvalidFormat {
155 message: "Field 21E reference cannot start or end with '/'".to_string(),
156 });
157 }
158
159 if reference.contains("//") {
160 return Err(ParseError::InvalidFormat {
161 message: "Field 21E reference cannot contain consecutive slashes '//'".to_string(),
162 });
163 }
164
165 Ok(Field21E { reference })
166 }
167
168 fn to_swift_string(&self) -> String {
169 format!(":21E:{}", self.reference)
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct Field21F {
180 pub reference: String,
182}
183
184impl SwiftField for Field21F {
185 fn parse(input: &str) -> crate::Result<Self>
186 where
187 Self: Sized,
188 {
189 let reference = parse_max_length(input, 16, "Field 21F reference")?;
190 parse_swift_chars(&reference, "Field 21F reference")?;
191
192 if reference.starts_with('/') || reference.ends_with('/') {
193 return Err(ParseError::InvalidFormat {
194 message: "Field 21F reference cannot start or end with '/'".to_string(),
195 });
196 }
197
198 if reference.contains("//") {
199 return Err(ParseError::InvalidFormat {
200 message: "Field 21F reference cannot contain consecutive slashes '//'".to_string(),
201 });
202 }
203
204 Ok(Field21F { reference })
205 }
206
207 fn to_swift_string(&self) -> String {
208 format!(":21F:{}", self.reference)
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218pub struct Field21R {
219 pub reference: String,
221}
222
223impl SwiftField for Field21R {
224 fn parse(input: &str) -> crate::Result<Self>
225 where
226 Self: Sized,
227 {
228 let reference = parse_max_length(input, 16, "Field 21R reference")?;
229 parse_swift_chars(&reference, "Field 21R reference")?;
230
231 if reference.starts_with('/') || reference.ends_with('/') {
232 return Err(ParseError::InvalidFormat {
233 message: "Field 21R reference cannot start or end with '/'".to_string(),
234 });
235 }
236
237 if reference.contains("//") {
238 return Err(ParseError::InvalidFormat {
239 message: "Field 21R reference cannot contain consecutive slashes '//'".to_string(),
240 });
241 }
242
243 Ok(Field21R { reference })
244 }
245
246 fn to_swift_string(&self) -> String {
247 format!(":21R:{}", self.reference)
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_field21_no_option() {
257 let field = Field21NoOption::parse("REF20240719001").unwrap();
258 assert_eq!(field.reference, "REF20240719001");
259 assert_eq!(field.to_swift_string(), ":21:REF20240719001");
260
261 assert!(Field21NoOption::parse("1234567890ABCDEF").is_ok());
263 assert!(Field21NoOption::parse("1234567890ABCDEFG").is_err());
264
265 assert!(Field21NoOption::parse("/REF123").is_err());
267 assert!(Field21NoOption::parse("REF123/").is_err());
268 assert!(Field21NoOption::parse("REF//123").is_err());
269 }
270
271 #[test]
272 fn test_field21c() {
273 let field = Field21C::parse("TREASURY/SWAP/2024/07/19/001").unwrap();
274 assert_eq!(field.reference, "TREASURY/SWAP/2024/07/19/001");
275
276 let long_ref = "12345678901234567890123456789012345";
278 assert!(Field21C::parse(long_ref).is_ok());
279 assert!(Field21C::parse(&format!("{}X", long_ref)).is_err());
280 }
281
282 #[test]
283 fn test_field21d() {
284 let field = Field21D::parse("FX-DEAL-20240719-EUR-USD").unwrap();
285 assert_eq!(field.reference, "FX-DEAL-20240719-EUR-USD");
286 assert_eq!(field.to_swift_string(), ":21D:FX-DEAL-20240719-EUR-USD");
287 }
288
289 #[test]
290 fn test_field21e() {
291 let field = Field21E::parse("ORIGINAL-REF-123456").unwrap();
292 assert_eq!(field.reference, "ORIGINAL-REF-123456");
293 }
294
295 #[test]
296 fn test_field21f() {
297 let field = Field21F::parse("BATCH-20240719").unwrap();
298 assert_eq!(field.reference, "BATCH-20240719");
299
300 assert!(Field21F::parse("1234567890ABCDEF").is_ok());
302 assert!(Field21F::parse("1234567890ABCDEFG").is_err());
303 }
304
305 #[test]
306 fn test_field21r() {
307 let field = Field21R::parse("FILE-REF-001").unwrap();
308 assert_eq!(field.reference, "FILE-REF-001");
309 assert_eq!(field.to_swift_string(), ":21R:FILE-REF-001");
310 }
311}