swift_mt_message_macros/component/
parser.rs

1use syn::{Attribute, FieldsNamed};
2
3/// Component specification parsed from #[component] attribute
4#[derive(Debug, Clone)]
5pub struct ComponentSpec {
6    /// SWIFT format specification (e.g., "6!n", "3!a", "15d")
7    pub format: String,
8    /// Whether this component is optional
9    pub optional: bool,
10    /// Validation rules to apply (e.g., ["date_format", "currency_code"])
11    pub validation_rules: Vec<String>,
12    /// Field name this component applies to
13    pub field_name: String,
14    /// Field type
15    pub field_type: syn::Type,
16}
17
18/// Specification for a sequence field
19#[derive(Debug, Clone)]
20pub struct SequenceSpec {
21    /// Sequence identifier (e.g., "A", "B")
22    pub sequence_id: String,
23    /// Whether this sequence is repetitive
24    pub repetitive: bool,
25    /// Field name this sequence applies to
26    pub field_name: String,
27    /// Field type (must be Vec<T> for repetitive sequences)
28    pub field_type: syn::Type,
29}
30
31/// Field specification - either a component or a sequence
32#[derive(Debug, Clone)]
33pub enum FieldSpec {
34    Component(ComponentSpec),
35    Sequence(SequenceSpec),
36}
37
38/// Parse field specifications from struct fields
39pub fn parse_field_specs(fields: &FieldsNamed) -> Result<Vec<FieldSpec>, String> {
40    let mut specs = Vec::new();
41
42    for field in &fields.named {
43        let field_name = field
44            .ident
45            .as_ref()
46            .ok_or("Field must have a name")?
47            .to_string();
48
49        // Check for #[sequence(...)] attribute first
50        if let Some(sequence_attr) = find_sequence_attribute(&field.attrs) {
51            let spec = parse_sequence_attribute(sequence_attr, field_name, field.ty.clone())?;
52            specs.push(FieldSpec::Sequence(spec));
53        }
54        // Then check for #[component(...)] attribute
55        else if let Some(component_attr) = find_component_attribute(&field.attrs) {
56            let spec = parse_component_attribute(component_attr, field_name, field.ty.clone())?;
57            specs.push(FieldSpec::Component(spec));
58        }
59        // Check for #[field(...)] attribute (new unified approach)
60        else if let Some(field_attr) = find_field_attribute(&field.attrs) {
61            let spec = parse_field_attribute(field_attr, field_name, field.ty.clone())?;
62            specs.push(spec);
63        }
64    }
65
66    if specs.is_empty() {
67        return Err(
68            "No field attributes found. SwiftMessage requires field specifications.".to_string(),
69        );
70    }
71
72    Ok(specs)
73}
74
75/// Parse component specifications from struct fields (legacy support)
76pub fn parse_component_specs(fields: &FieldsNamed) -> Result<Vec<ComponentSpec>, String> {
77    let field_specs = parse_field_specs(fields)?;
78
79    let components: Vec<ComponentSpec> = field_specs
80        .into_iter()
81        .filter_map(|spec| match spec {
82            FieldSpec::Component(comp) => Some(comp),
83            FieldSpec::Sequence(_) => None,
84        })
85        .collect();
86
87    if components.is_empty() {
88        return Err("No component specifications found.".to_string());
89    }
90
91    Ok(components)
92}
93
94/// Find the #[sequence(...)] attribute in a list of attributes
95fn find_sequence_attribute(attrs: &[Attribute]) -> Option<&Attribute> {
96    attrs.iter().find(|attr| attr.path().is_ident("sequence"))
97}
98
99/// Find the #[field(...)] attribute in a list of attributes
100fn find_field_attribute(attrs: &[Attribute]) -> Option<&Attribute> {
101    attrs.iter().find(|attr| attr.path().is_ident("field"))
102}
103
104/// Parse a #[sequence(...)] attribute
105fn parse_sequence_attribute(
106    attr: &Attribute,
107    field_name: String,
108    field_type: syn::Type,
109) -> Result<SequenceSpec, String> {
110    let mut sequence_id = None;
111    let mut repetitive = false;
112
113    if let syn::Meta::List(meta_list) = &attr.meta {
114        let tokens_str = meta_list.tokens.to_string();
115
116        // Parse sequence ID (first quoted string)
117        if let Some(start) = tokens_str.find('"') {
118            if let Some(end) = tokens_str[start + 1..].find('"') {
119                sequence_id = Some(tokens_str[start + 1..start + 1 + end].to_string());
120            }
121        }
122
123        // Check for repetitive flag
124        if tokens_str.contains("repetitive") {
125            repetitive = true;
126        }
127    }
128
129    let sequence_id = sequence_id.ok_or("Sequence must specify an ID (e.g., \"A\", \"B\")")?;
130
131    Ok(SequenceSpec {
132        sequence_id,
133        repetitive,
134        field_name,
135        field_type,
136    })
137}
138
139/// Parse a #[field(...)] attribute (unified approach)
140fn parse_field_attribute(
141    attr: &Attribute,
142    field_name: String,
143    field_type: syn::Type,
144) -> Result<FieldSpec, String> {
145    if let syn::Meta::List(meta_list) = &attr.meta {
146        let tokens_str = meta_list.tokens.to_string();
147
148        // Check if this is a sequence specification
149        if tokens_str.contains("repetitive") || tokens_str.contains("sequence") {
150            // Parse as sequence
151            let sequence_spec =
152                parse_sequence_from_field_attr(&tokens_str, field_name, field_type)?;
153            Ok(FieldSpec::Sequence(sequence_spec))
154        } else {
155            // Parse as component
156            let component_spec =
157                parse_component_from_field_attr(&tokens_str, field_name, field_type)?;
158            Ok(FieldSpec::Component(component_spec))
159        }
160    } else {
161        Err("Field attribute must have parameters".to_string())
162    }
163}
164
165/// Parse sequence specification from #[field(...)] attribute
166fn parse_sequence_from_field_attr(
167    tokens_str: &str,
168    field_name: String,
169    field_type: syn::Type,
170) -> Result<SequenceSpec, String> {
171    let mut sequence_id = None;
172    let mut repetitive = false;
173
174    // Parse sequence ID (first quoted string)
175    if let Some(start) = tokens_str.find('"') {
176        if let Some(end) = tokens_str[start + 1..].find('"') {
177            sequence_id = Some(tokens_str[start + 1..start + 1 + end].to_string());
178        }
179    }
180
181    // Check for repetitive flag
182    if tokens_str.contains("repetitive") {
183        repetitive = true;
184    }
185
186    let sequence_id = sequence_id.unwrap_or_else(|| field_name.clone());
187
188    Ok(SequenceSpec {
189        sequence_id,
190        repetitive,
191        field_name,
192        field_type,
193    })
194}
195
196/// Parse component specification from #[field(...)] attribute
197fn parse_component_from_field_attr(
198    tokens_str: &str,
199    field_name: String,
200    field_type: syn::Type,
201) -> Result<ComponentSpec, String> {
202    let mut format = None;
203    let mut optional = false;
204    let mut validation_rules = Vec::new();
205
206    // Parse format string (first quoted string)
207    if let Some(start) = tokens_str.find('"') {
208        if let Some(end) = tokens_str[start + 1..].find('"') {
209            format = Some(tokens_str[start + 1..start + 1 + end].to_string());
210        }
211    }
212
213    // Check for optional/mandatory flags
214    if tokens_str.contains("optional") {
215        optional = true;
216    }
217    // mandatory is the default, but we can be explicit
218
219    // Parse validation rules
220    if tokens_str.contains("validate") {
221        validation_rules = parse_validation_rules_from_string(tokens_str)?;
222    }
223
224    let format = format.ok_or("Field must specify a format string or be a sequence")?;
225
226    Ok(ComponentSpec {
227        format,
228        optional,
229        validation_rules,
230        field_name,
231        field_type,
232    })
233}
234
235/// Parse validation rules from the token string
236fn parse_validation_rules_from_string(tokens_str: &str) -> Result<Vec<String>, String> {
237    let mut rules = Vec::new();
238
239    // Look for validate = [...]
240    if let Some(validate_pos) = tokens_str.find("validate") {
241        let after_validate = &tokens_str[validate_pos..];
242
243        // Look for opening bracket
244        if let Some(bracket_start) = after_validate.find('[') {
245            if let Some(bracket_end) = after_validate.find(']') {
246                let rules_content = &after_validate[bracket_start + 1..bracket_end];
247
248                // Split by comma and clean up quotes
249                for rule in rules_content.split(',') {
250                    let clean_rule = rule.trim().trim_matches('"').trim();
251                    if !clean_rule.is_empty() {
252                        rules.push(clean_rule.to_string());
253                    }
254                }
255            }
256        } else {
257            // Look for single quoted rule: validate = "rule"
258            let after_equals = if let Some(eq_pos) = after_validate.find('=') {
259                &after_validate[eq_pos + 1..]
260            } else {
261                after_validate
262            };
263
264            if let Some(quote_start) = after_equals.find('"') {
265                if let Some(quote_end) = after_equals[quote_start + 1..].find('"') {
266                    let rule = &after_equals[quote_start + 1..quote_start + 1 + quote_end];
267                    rules.push(rule.to_string());
268                }
269            }
270        }
271    }
272
273    Ok(rules)
274}
275
276/// Generate the combined format specification from all components
277pub fn derive_format_spec(components: &[ComponentSpec]) -> String {
278    components
279        .iter()
280        .map(|comp| {
281            if comp.optional {
282                format!("[{}]", comp.format)
283            } else {
284                comp.format.clone()
285            }
286        })
287        .collect::<Vec<_>>()
288        .join("")
289}
290
291/// Check if a field type is Option<T>
292pub fn is_option_type(ty: &syn::Type) -> bool {
293    if let syn::Type::Path(type_path) = ty {
294        if let Some(segment) = type_path.path.segments.last() {
295            return segment.ident == "Option";
296        }
297    }
298    false
299}
300
301/// Extract the inner type from Option<T>
302pub fn extract_option_inner_type(ty: &syn::Type) -> Option<&syn::Type> {
303    if let syn::Type::Path(type_path) = ty {
304        if let Some(segment) = type_path.path.segments.last() {
305            if segment.ident == "Option" {
306                if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
307                    if let Some(syn::GenericArgument::Type(inner_type)) = args.args.first() {
308                        return Some(inner_type);
309                    }
310                }
311            }
312        }
313    }
314    None
315}
316
317/// Check if a field type is Vec<T>
318pub fn is_vec_type(ty: &syn::Type) -> bool {
319    if let syn::Type::Path(type_path) = ty {
320        if let Some(segment) = type_path.path.segments.last() {
321            return segment.ident == "Vec";
322        }
323    }
324    false
325}
326
327/// Extract the inner type from Vec<T>
328pub fn extract_vec_inner_type(ty: &syn::Type) -> Option<&syn::Type> {
329    if let syn::Type::Path(type_path) = ty {
330        if let Some(segment) = type_path.path.segments.last() {
331            if segment.ident == "Vec" {
332                if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
333                    if let Some(syn::GenericArgument::Type(inner_type)) = args.args.first() {
334                        return Some(inner_type);
335                    }
336                }
337            }
338        }
339    }
340    None
341}
342
343/// Check if a field type is u32
344pub fn is_u32_type(ty: &syn::Type) -> bool {
345    if let syn::Type::Path(type_path) = ty {
346        if let Some(segment) = type_path.path.segments.last() {
347            return segment.ident == "u32";
348        }
349    }
350    false
351}
352
353/// Check if a field type is f64
354pub fn is_f64_type(ty: &syn::Type) -> bool {
355    if let syn::Type::Path(type_path) = ty {
356        if let Some(segment) = type_path.path.segments.last() {
357            return segment.ident == "f64";
358        }
359    }
360    false
361}
362
363/// Get the base type for a field, handling Option<T> and Vec<T> wrappers
364pub fn get_base_type(ty: &syn::Type) -> &syn::Type {
365    // First check if it's Option<T>
366    if let Some(inner) = extract_option_inner_type(ty) {
367        // Could be Option<Vec<T>> or Option<u32>, etc.
368        return get_base_type(inner);
369    }
370
371    // Then check if it's Vec<T>
372    if let Some(inner) = extract_vec_inner_type(ty) {
373        return inner;
374    }
375
376    // Otherwise return the type as-is
377    ty
378}
379
380/// Check if a field type is NaiveDate
381pub fn is_naive_date_type(ty: &syn::Type) -> bool {
382    if let syn::Type::Path(type_path) = ty {
383        if let Some(segment) = type_path.path.segments.last() {
384            return segment.ident == "NaiveDate";
385        }
386    }
387    false
388}
389
390/// Check if a field type is NaiveTime
391pub fn is_naive_time_type(ty: &syn::Type) -> bool {
392    if let syn::Type::Path(type_path) = ty {
393        if let Some(segment) = type_path.path.segments.last() {
394            return segment.ident == "NaiveTime";
395        }
396    }
397    false
398}
399
400/// Check if a field type is char
401pub fn is_char_type(ty: &syn::Type) -> bool {
402    if let syn::Type::Path(type_path) = ty {
403        if let Some(segment) = type_path.path.segments.last() {
404            return segment.ident == "char";
405        }
406    }
407    false
408}
409
410/// Check if a field type is i32
411pub fn is_i32_type(ty: &syn::Type) -> bool {
412    if let syn::Type::Path(type_path) = ty {
413        if let Some(segment) = type_path.path.segments.last() {
414            return segment.ident == "i32";
415        }
416    }
417    false
418}
419
420/// Check if a field type is u8
421pub fn is_u8_type(ty: &syn::Type) -> bool {
422    if let syn::Type::Path(type_path) = ty {
423        if let Some(segment) = type_path.path.segments.last() {
424            return segment.ident == "u8";
425        }
426    }
427    false
428}
429
430/// Check if a field type is bool
431pub fn is_bool_type(ty: &syn::Type) -> bool {
432    if let syn::Type::Path(type_path) = ty {
433        if let Some(segment) = type_path.path.segments.last() {
434            return segment.ident == "bool";
435        }
436    }
437    false
438}
439
440/// Check if a type is likely a SwiftMessage (custom struct, not a field type)
441pub fn is_swift_message_type(ty: &syn::Type) -> bool {
442    if let syn::Type::Path(type_path) = ty {
443        if let Some(segment) = type_path.path.segments.last() {
444            let type_name = segment.ident.to_string();
445            // SwiftMessage types typically start with MT or are custom structs
446            // They don't start with Generic or Field prefixes
447            return type_name.starts_with("MT")
448                || (!type_name.starts_with("Generic")
449                    && !type_name.starts_with("Field")
450                    && !is_primitive_type(&type_name));
451        }
452    }
453    false
454}
455
456/// Check if a type name represents a primitive type
457fn is_primitive_type(type_name: &str) -> bool {
458    matches!(
459        type_name,
460        "u8" | "u16"
461            | "u32"
462            | "u64"
463            | "usize"
464            | "i8"
465            | "i16"
466            | "i32"
467            | "i64"
468            | "isize"
469            | "f32"
470            | "f64"
471            | "bool"
472            | "char"
473            | "String"
474            | "NaiveDate"
475            | "NaiveTime"
476            | "NaiveDateTime"
477    )
478}
479
480/// Check if a Vec<T> contains SwiftMessage types
481pub fn is_vec_of_swift_messages(ty: &syn::Type) -> bool {
482    if let Some(inner_type) = extract_vec_inner_type(ty) {
483        return is_swift_message_type(inner_type);
484    }
485    false
486}
487
488/// Find the #[component] attribute in a list of attributes
489fn find_component_attribute(attrs: &[Attribute]) -> Option<&Attribute> {
490    attrs.iter().find(|attr| attr.path().is_ident("component"))
491}
492
493/// Parse a single #[component(...)] attribute using a simplified approach
494fn parse_component_attribute(
495    attr: &Attribute,
496    field_name: String,
497    field_type: syn::Type,
498) -> Result<ComponentSpec, String> {
499    let mut format = None;
500    let mut optional = false;
501    let mut validation_rules = Vec::new();
502
503    // Simple parsing approach: parse the tokens manually
504    if let syn::Meta::List(meta_list) = &attr.meta {
505        let tokens_str = meta_list.tokens.to_string();
506
507        // Parse format string (first quoted string)
508        if let Some(start) = tokens_str.find('"') {
509            if let Some(end) = tokens_str[start + 1..].find('"') {
510                format = Some(tokens_str[start + 1..start + 1 + end].to_string());
511            }
512        }
513
514        // Check for optional flag
515        if tokens_str.contains("optional") {
516            optional = true;
517        }
518
519        // Parse validation rules (look for validate = [...])
520        if tokens_str.contains("validate") {
521            validation_rules = parse_validation_rules_from_string(&tokens_str)?;
522        }
523    }
524
525    let format = format.ok_or("Component must specify a format string")?;
526
527    Ok(ComponentSpec {
528        format,
529        optional,
530        validation_rules,
531        field_name,
532        field_type,
533    })
534}