swift_mt_message_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 Attribute, Data, DeriveInput, Fields, GenericArgument, Meta, PathArguments, Type,
5 parse_macro_input,
6};
7
8#[proc_macro_derive(SwiftField, attributes(format, field_option))]
10pub fn derive_swift_field(input: TokenStream) -> TokenStream {
11 let input = parse_macro_input!(input as DeriveInput);
12 let name = &input.ident;
13
14 match &input.data {
15 Data::Struct(data) => {
16 match &data.fields {
17 Fields::Named(fields) => {
18 let field_parsing = fields.named.iter().map(|field| {
20 let field_name = &field.ident;
21 let _field_type = &field.ty;
22
23 let format_spec = extract_format_attribute(&field.attrs);
25
26 match format_spec.as_deref() {
27 Some("16x") => quote! {
28 #field_name: content.trim().to_string()
29 },
30 Some("4*35x") => quote! {
31 #field_name: content.split('\n').map(|s| s.trim().to_string()).collect()
32 },
33 Some("BIC") => quote! {
34 #field_name: crate::common::BIC::new(content.trim().to_string())
35 },
36 Some("structured_party_identifier") => quote! {
37 #field_name: content.trim().to_string() },
39 Some(spec) if spec.ends_with("x") => quote! {
40 #field_name: content.trim().to_string()
41 },
42 _ => quote! {
43 #field_name: content.trim().to_string()
44 }
45 }
46 });
47
48 let validation_logic = fields.named.iter().map(|field| {
49 let field_name = &field.ident;
50 let format_spec = extract_format_attribute(&field.attrs);
51
52 match format_spec.as_deref() {
53 Some("16x") => quote! {
54 if self.#field_name.len() > 16 {
55 errors.push(crate::ValidationError::LengthValidation {
56 field_tag: stringify!(#name).to_string(),
57 expected: "max 16 characters".to_string(),
58 actual: self.#field_name.len(),
59 });
60 }
61 },
62 Some("BIC") => quote! {
63 if !self.#field_name.validate() {
64 errors.push(crate::ValidationError::FormatValidation {
65 field_tag: stringify!(#name).to_string(),
66 message: "Invalid BIC format".to_string(),
67 });
68 }
69 },
70 _ => quote! {
71 if self.#field_name.is_empty() {
73 errors.push(crate::ValidationError::ValueValidation {
74 field_tag: stringify!(#name).to_string(),
75 message: "Field cannot be empty".to_string(),
76 });
77 }
78 },
79 }
80 });
81
82 let format_spec = extract_format_attribute_from_struct(&input.attrs)
83 .unwrap_or_else(|| "custom".to_string());
84
85 let field_tag = name
87 .to_string()
88 .strip_prefix("Field")
89 .unwrap_or(&name.to_string())
90 .to_uppercase();
91
92 let to_swift_string_impl = if fields.named.len() == 1 {
94 let field = fields.named.first().unwrap();
96 let field_name = &field.ident;
97 let _field_type = &field.ty;
98 let format_spec = extract_format_attribute(&field.attrs);
99
100 match format_spec.as_deref() {
101 Some("BIC") => quote! {
102 fn to_swift_string(&self) -> String {
103 format!(":{}:{}", #field_tag, self.#field_name.value)
104 }
105 },
106 Some("4*35x") => quote! {
107 fn to_swift_string(&self) -> String {
108 format!(":{}:{}", #field_tag, self.#field_name.join("\n"))
109 }
110 },
111 _ => quote! {
112 fn to_swift_string(&self) -> String {
113 format!(":{}:{}", #field_tag, self.#field_name)
114 }
115 },
116 }
117 } else {
118 quote! {
120 fn to_swift_string(&self) -> String {
121 format!(":{}:{:?}", #field_tag, self)
123 }
124 }
125 };
126
127 let expanded = quote! {
128 impl crate::SwiftField for #name {
129 fn parse(value: &str) -> crate::Result<Self> {
130 let value = value.trim();
131
132 let content = if value.starts_with(&format!(":{}:", #field_tag)) {
134 &value[#field_tag.len() + 2..] } else if value.starts_with(&format!("{}:", #field_tag)) {
136 &value[#field_tag.len() + 1..] } else {
138 value };
140
141 Ok(Self {
142 #(#field_parsing,)*
143 })
144 }
145
146 #to_swift_string_impl
147
148 fn validate(&self) -> crate::ValidationResult {
149 let mut errors = Vec::new();
150 #(#validation_logic)*
151
152 crate::ValidationResult {
153 is_valid: errors.is_empty(),
154 errors,
155 warnings: Vec::new(),
156 }
157 }
158
159 fn format_spec() -> &'static str {
160 #format_spec
161 }
162 }
163 };
164
165 TokenStream::from(expanded)
166 }
167 _ => {
168 panic!("SwiftField can only be derived for structs with named fields");
169 }
170 }
171 }
172 Data::Enum(data) => {
173 let variants = &data.variants;
175
176 let parse_arms = variants.iter().map(|variant| {
177 let variant_name = &variant.ident;
178 let option_letter = extract_field_option_attribute(&variant.attrs)
179 .unwrap_or_else(|| variant_name.to_string());
180
181 if let syn::Fields::Unnamed(fields) = &variant.fields {
183 if let Some(field) = fields.unnamed.first() {
184 let field_type = &field.ty;
185 quote! {
186 #option_letter => {
187 let inner = <#field_type>::parse(&value[1..])?;
188 Ok(#name::#variant_name(inner))
189 }
190 }
191 } else {
192 quote! {
193 #option_letter => Ok(#name::#variant_name)
194 }
195 }
196 } else {
197 quote! {
198 #option_letter => Ok(#name::#variant_name)
199 }
200 }
201 });
202
203 let to_string_arms = variants.iter().map(|variant| {
204 let variant_name = &variant.ident;
205 let option_letter = extract_field_option_attribute(&variant.attrs)
206 .unwrap_or_else(|| variant_name.to_string());
207
208 if let syn::Fields::Unnamed(fields) = &variant.fields {
209 if !fields.unnamed.is_empty() {
210 quote! {
211 #name::#variant_name(inner) => {
212 format!("{}{}", #option_letter, inner.to_swift_string())
213 }
214 }
215 } else {
216 quote! {
217 #name::#variant_name => #option_letter.to_string()
218 }
219 }
220 } else {
221 quote! {
222 #name::#variant_name => #option_letter.to_string()
223 }
224 }
225 });
226
227 let validate_arms = variants.iter().map(|variant| {
228 let variant_name = &variant.ident;
229
230 if let syn::Fields::Unnamed(fields) = &variant.fields {
231 if !fields.unnamed.is_empty() {
232 quote! {
233 #name::#variant_name(inner) => inner.validate()
234 }
235 } else {
236 quote! {
237 #name::#variant_name => crate::ValidationResult::valid()
238 }
239 }
240 } else {
241 quote! {
242 #name::#variant_name => crate::ValidationResult::valid()
243 }
244 }
245 });
246
247 let expanded = quote! {
248 impl crate::SwiftField for #name {
249 fn parse(value: &str) -> crate::Result<Self> {
250 let option = value.chars().next().map(|c| c.to_string()).unwrap_or_default();
251
252 match option.as_str() {
253 #(#parse_arms)*
254 _ => Err(crate::ParseError::InvalidFieldFormat {
255 field_tag: stringify!(#name).to_string(),
256 message: format!("Unknown option: {:?}", option),
257 })
258 }
259 }
260
261 fn to_swift_string(&self) -> String {
262 match self {
263 #(#to_string_arms)*
264 }
265 }
266
267 fn validate(&self) -> crate::ValidationResult {
268 match self {
269 #(#validate_arms)*
270 }
271 }
272
273 fn format_spec() -> &'static str {
274 "option"
275 }
276 }
277 };
278
279 TokenStream::from(expanded)
280 }
281 Data::Union(_) => {
282 panic!("SwiftField cannot be derived for unions");
283 }
284 }
285}
286
287#[proc_macro_attribute]
289pub fn swift_serde(_args: TokenStream, input: TokenStream) -> TokenStream {
290 let mut input = parse_macro_input!(input as DeriveInput);
291
292 match &mut input.data {
293 Data::Struct(data) => {
294 match &mut data.fields {
295 Fields::Named(fields) => {
296 for field in &mut fields.named {
298 let field_tag = extract_field_attribute(&field.attrs)
299 .expect("All fields must have #[field(\"tag\")]");
300
301 let has_serde_rename = field.attrs.iter().any(|attr| {
303 if attr.path().is_ident("serde") {
304 if let Meta::List(meta_list) = &attr.meta {
305 return meta_list.tokens.to_string().contains("rename");
306 }
307 }
308 false
309 });
310
311 if !has_serde_rename {
313 let serde_rename: Attribute = syn::parse_quote! {
314 #[serde(rename = #field_tag)]
315 };
316 field.attrs.push(serde_rename);
317 }
318 }
319
320 TokenStream::from(quote! { #input })
321 }
322 _ => {
323 panic!("swift_serde can only be applied to structs with named fields");
324 }
325 }
326 }
327 _ => {
328 panic!("swift_serde can only be applied to structs");
329 }
330 }
331}
332
333#[proc_macro_derive(SwiftMessage, attributes(swift_message, field))]
335pub fn derive_swift_message(input: TokenStream) -> TokenStream {
336 let input = parse_macro_input!(input as DeriveInput);
337 let name = &input.ident;
338
339 let message_type = extract_message_type_attribute(&input.attrs)
341 .expect("SwiftMessage requires #[swift_message(mt = \"...\")]");
342
343 match &input.data {
344 Data::Struct(data) => {
345 match &data.fields {
346 Fields::Named(fields) => {
347 let mut required_field_parsing = Vec::new();
349 let mut optional_field_parsing = Vec::new();
350 let mut required_field_serialization = Vec::new();
351 let mut optional_field_serialization = Vec::new();
352 let mut required_field_tags = Vec::new();
353 let mut optional_field_tags = Vec::new();
354
355 for field in &fields.named {
356 let field_name = &field.ident;
357 let field_type = &field.ty;
358 let field_tag = extract_field_attribute(&field.attrs)
359 .expect("All fields must have #[field(\"tag\")]");
360
361 if is_option_type(field_type) {
362 let inner_type = extract_option_inner_type(field_type)
363 .expect("Failed to extract inner type from Option");
364
365 if is_vec_type(inner_type) {
366 let vec_inner_type = extract_vec_inner_type(inner_type)
368 .expect("Failed to extract inner type from Vec");
369
370 optional_field_parsing.push(quote! {
371 #field_name: if let Some(field_values) = fields.get(#field_tag) {
372 let mut parsed_fields = Vec::new();
373 for field_value in field_values {
374 parsed_fields.push(<#vec_inner_type as crate::SwiftField>::parse(field_value)?);
375 }
376 if parsed_fields.is_empty() {
377 None
378 } else {
379 Some(parsed_fields)
380 }
381 } else {
382 None
383 }
384 });
385
386 optional_field_serialization.push(quote! {
387 if let Some(ref field_values) = self.#field_name {
388 let mut serialized_values = Vec::new();
389 for field_value in field_values {
390 serialized_values.push(crate::SwiftField::to_swift_string(field_value));
391 }
392 fields.insert(#field_tag.to_string(), serialized_values);
393 }
394 });
395 } else {
396 optional_field_parsing.push(quote! {
398 #field_name: if let Some(field_value) = fields.get(#field_tag) {
399 Some(<#inner_type as crate::SwiftField>::parse(&field_value[0])?)
400 } else {
401 None
402 }
403 });
404
405 optional_field_serialization.push(quote! {
406 if let Some(ref field_value) = self.#field_name {
407 fields.insert(#field_tag.to_string(), vec![crate::SwiftField::to_swift_string(field_value)]);
408 }
409 });
410 }
411
412 optional_field_tags.push(quote! { #field_tag });
413 } else if is_vec_type(field_type) {
414 let vec_inner_type = extract_vec_inner_type(field_type)
416 .expect("Failed to extract inner type from Vec");
417
418 required_field_parsing.push(quote! {
419 #field_name: {
420 let field_values = fields.get(#field_tag)
421 .ok_or_else(|| crate::ParseError::MissingRequiredField {
422 field_tag: #field_tag.to_string(),
423 })?;
424 let mut parsed_fields = Vec::new();
425 for field_value in field_values {
426 parsed_fields.push(<#vec_inner_type as crate::SwiftField>::parse(field_value)?);
427 }
428 parsed_fields
429 }
430 });
431
432 required_field_serialization.push(quote! {
433 {
434 let mut serialized_values = Vec::new();
435 for field_value in &self.#field_name {
436 serialized_values.push(crate::SwiftField::to_swift_string(field_value));
437 }
438 fields.insert(#field_tag.to_string(), serialized_values);
439 }
440 });
441
442 required_field_tags.push(quote! { #field_tag });
443 } else {
444 required_field_parsing.push(quote! {
446 #field_name: <#field_type as crate::SwiftField>::parse(
447 &fields.get(#field_tag)
448 .ok_or_else(|| crate::ParseError::MissingRequiredField {
449 field_tag: #field_tag.to_string(),
450 })?[0]
451 )?
452 });
453
454 required_field_serialization.push(quote! {
455 fields.insert(#field_tag.to_string(), vec![crate::SwiftField::to_swift_string(&self.#field_name)]);
456 });
457
458 required_field_tags.push(quote! { #field_tag });
459 }
460 }
461
462 let all_field_parsing = required_field_parsing
464 .into_iter()
465 .chain(optional_field_parsing);
466
467 let all_field_serialization = required_field_serialization
469 .into_iter()
470 .chain(optional_field_serialization);
471
472 let expanded = quote! {
473 impl crate::SwiftMessageBody for #name {
474 fn message_type() -> &'static str {
475 #message_type
476 }
477
478 fn from_fields(fields: std::collections::HashMap<String, Vec<String>>) -> crate::Result<Self> {
479 Ok(Self {
480 #(#all_field_parsing,)*
481 })
482 }
483
484 fn to_fields(&self) -> std::collections::HashMap<String, Vec<String>> {
485 let mut fields = std::collections::HashMap::new();
486 #(#all_field_serialization)*
487 fields
488 }
489
490 fn required_fields() -> Vec<&'static str> {
491 vec![#(#required_field_tags),*]
492 }
493
494 fn optional_fields() -> Vec<&'static str> {
495 vec![#(#optional_field_tags),*]
496 }
497 }
498 };
499
500 TokenStream::from(expanded)
501 }
502 _ => {
503 panic!("SwiftMessage can only be derived for structs with named fields");
504 }
505 }
506 }
507 _ => {
508 panic!("SwiftMessage can only be derived for structs");
509 }
510 }
511}
512
513fn is_option_type(ty: &Type) -> bool {
517 if let Type::Path(type_path) = ty {
518 if let Some(segment) = type_path.path.segments.last() {
519 return segment.ident == "Option";
520 }
521 }
522 false
523}
524
525fn is_vec_type(ty: &Type) -> bool {
527 if let Type::Path(type_path) = ty {
528 if let Some(segment) = type_path.path.segments.last() {
529 return segment.ident == "Vec";
530 }
531 }
532 false
533}
534
535fn extract_option_inner_type(ty: &Type) -> Option<&Type> {
537 if let Type::Path(type_path) = ty {
538 if let Some(segment) = type_path.path.segments.last() {
539 if segment.ident == "Option" {
540 if let PathArguments::AngleBracketed(args) = &segment.arguments {
541 if let Some(GenericArgument::Type(inner_type)) = args.args.first() {
542 return Some(inner_type);
543 }
544 }
545 }
546 }
547 }
548 None
549}
550
551fn extract_vec_inner_type(ty: &Type) -> Option<&Type> {
553 if let Type::Path(type_path) = ty {
554 if let Some(segment) = type_path.path.segments.last() {
555 if segment.ident == "Vec" {
556 if let PathArguments::AngleBracketed(args) = &segment.arguments {
557 if let Some(GenericArgument::Type(inner_type)) = args.args.first() {
558 return Some(inner_type);
559 }
560 }
561 }
562 }
563 }
564 None
565}
566
567fn extract_format_attribute(attrs: &[Attribute]) -> Option<String> {
568 for attr in attrs {
569 if attr.path().is_ident("format") {
570 if let Meta::List(meta_list) = &attr.meta {
571 if let Some(nested) = meta_list.tokens.to_string().strip_prefix('"') {
572 if let Some(value) = nested.strip_suffix('"') {
573 return Some(value.to_string());
574 }
575 }
576 }
577 }
578 }
579 None
580}
581
582fn extract_format_attribute_from_struct(attrs: &[Attribute]) -> Option<String> {
583 extract_format_attribute(attrs)
584}
585
586fn extract_field_option_attribute(attrs: &[Attribute]) -> Option<String> {
587 for attr in attrs {
588 if attr.path().is_ident("field_option") {
589 if let Meta::List(meta_list) = &attr.meta {
590 if let Some(nested) = meta_list.tokens.to_string().strip_prefix('"') {
591 if let Some(value) = nested.strip_suffix('"') {
592 return Some(value.to_string());
593 }
594 }
595 }
596 }
597 }
598 None
599}
600
601fn extract_message_type_attribute(attrs: &[Attribute]) -> Option<String> {
602 for attr in attrs {
603 if attr.path().is_ident("swift_message") {
604 if let Meta::List(meta_list) = &attr.meta {
605 let tokens = meta_list.tokens.to_string();
606 if let Some(eq_pos) = tokens.find('=') {
608 let value_part = tokens[eq_pos + 1..].trim();
609 if let Some(nested) = value_part.strip_prefix('"') {
610 if let Some(value) = nested.strip_suffix('"') {
611 return Some(value.to_string());
612 }
613 }
614 }
615 }
616 }
617 }
618 None
619}
620
621fn extract_field_attribute(attrs: &[Attribute]) -> Option<String> {
622 for attr in attrs {
623 if attr.path().is_ident("field") {
624 if let Meta::List(meta_list) = &attr.meta {
625 if let Some(nested) = meta_list.tokens.to_string().strip_prefix('"') {
626 if let Some(value) = nested.strip_suffix('"') {
627 return Some(value.to_string());
628 }
629 }
630 }
631 }
632 }
633 None
634}