swift_mt_message_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    Attribute, Data, DeriveInput, Fields, GenericArgument, Meta, PathArguments, Type,
5    parse_macro_input,
6};
7
8/// Derive macro for SwiftField trait implementation
9#[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                    // Handle struct with named fields
19                    let field_parsing = fields.named.iter().map(|field| {
20                        let field_name = &field.ident;
21                        let _field_type = &field.ty;
22
23                        // Look for #[format("...")] attribute
24                        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() // TODO: implement proper parsing
38                            },
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                                // Basic validation
72                                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                    // Extract field tag from struct name (e.g., "Field20" -> "20")
86                    let field_tag = name
87                        .to_string()
88                        .strip_prefix("Field")
89                        .unwrap_or(&name.to_string())
90                        .to_uppercase();
91
92                    // Generate to_swift_string implementation based on field structure
93                    let to_swift_string_impl = if fields.named.len() == 1 {
94                        // Single field - handle different types appropriately
95                        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                        // Multiple fields - need custom logic per field type
119                        quote! {
120                            fn to_swift_string(&self) -> String {
121                                // Multi-field implementation - customize per field type
122                                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                                // Handle input that includes field tag prefix
133                                let content = if value.starts_with(&format!(":{}:", #field_tag)) {
134                                    &value[#field_tag.len() + 2..]  // Remove ":TAG:" prefix
135                                } else if value.starts_with(&format!("{}:", #field_tag)) {
136                                    &value[#field_tag.len() + 1..]  // Remove "TAG:" prefix
137                                } else {
138                                    value  // Use as-is if no prefix
139                                };
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            // Handle enum with field options
174            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                // Get the type inside the variant
182                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/// Attribute macro for adding serde rename attributes based on field tags
288#[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                    // Add serde rename attributes to each field based on their field tag
297                    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                        // Check if serde rename already exists
302                        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                        // Add serde rename attribute if it doesn't exist
312                        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/// Derive macro for SwiftMessage trait implementation
334#[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    // Extract message type from #[swift_message(mt = "103")]
340    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                    // Separate required and optional fields
348                    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                                // Optional Vec field: Option<Vec<T>>
367                                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 single field: Option<T>
397                                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                            // Required Vec field: Vec<T>
415                            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 single field: T
445                            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                    // Combine all field parsing
463                    let all_field_parsing = required_field_parsing
464                        .into_iter()
465                        .chain(optional_field_parsing);
466
467                    // Combine all field serialization
468                    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
513// Helper functions to extract attributes and handle Option types
514
515/// Check if a type is Option<T>
516fn 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
525/// Check if a type is Vec<T>
526fn 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
535/// Extract the inner type T from Option<T>
536fn 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
551/// Extract the inner type T from Vec<T>
552fn 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                // Parse: mt = "103"
607                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}