swift_mt_message_macros/component/
parser.rs1use syn::{Attribute, FieldsNamed};
2
3#[derive(Debug, Clone)]
5pub struct ComponentSpec {
6 pub format: String,
8 pub optional: bool,
10 pub validation_rules: Vec<String>,
12 pub field_name: String,
14 pub field_type: syn::Type,
16}
17
18#[derive(Debug, Clone)]
20pub struct SequenceSpec {
21 pub sequence_id: String,
23 pub repetitive: bool,
25 pub field_name: String,
27 pub field_type: syn::Type,
29}
30
31#[derive(Debug, Clone)]
33pub enum FieldSpec {
34 Component(ComponentSpec),
35 Sequence(SequenceSpec),
36}
37
38pub 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 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 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 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
75pub 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
94fn find_sequence_attribute(attrs: &[Attribute]) -> Option<&Attribute> {
96 attrs.iter().find(|attr| attr.path().is_ident("sequence"))
97}
98
99fn find_field_attribute(attrs: &[Attribute]) -> Option<&Attribute> {
101 attrs.iter().find(|attr| attr.path().is_ident("field"))
102}
103
104fn 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 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 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
139fn 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 if tokens_str.contains("repetitive") || tokens_str.contains("sequence") {
150 let sequence_spec =
152 parse_sequence_from_field_attr(&tokens_str, field_name, field_type)?;
153 Ok(FieldSpec::Sequence(sequence_spec))
154 } else {
155 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
165fn 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 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 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
196fn 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 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 if tokens_str.contains("optional") {
215 optional = true;
216 }
217 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
235fn parse_validation_rules_from_string(tokens_str: &str) -> Result<Vec<String>, String> {
237 let mut rules = Vec::new();
238
239 if let Some(validate_pos) = tokens_str.find("validate") {
241 let after_validate = &tokens_str[validate_pos..];
242
243 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 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 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
276pub 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
291pub 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
301pub 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
317pub 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
327pub 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
343pub 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
353pub 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
363pub fn get_base_type(ty: &syn::Type) -> &syn::Type {
365 if let Some(inner) = extract_option_inner_type(ty) {
367 return get_base_type(inner);
369 }
370
371 if let Some(inner) = extract_vec_inner_type(ty) {
373 return inner;
374 }
375
376 ty
378}
379
380pub 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
390pub 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
400pub 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
410pub 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
420pub 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
430pub 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
440pub 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 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
456fn 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
480pub 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
488fn find_component_attribute(attrs: &[Attribute]) -> Option<&Attribute> {
490 attrs.iter().find(|attr| attr.path().is_ident("component"))
491}
492
493fn 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 if let syn::Meta::List(meta_list) = &attr.meta {
505 let tokens_str = meta_list.tokens.to_string();
506
507 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 if tokens_str.contains("optional") {
516 optional = true;
517 }
518
519 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}