Skip to main content

tauri_typegen/analysis/
struct_parser.rs

1use crate::analysis::serde_parser::SerdeParser;
2use crate::analysis::type_resolver::TypeResolver;
3use crate::analysis::validator_parser::ValidatorParser;
4use crate::models::{EnumVariantInfo, EnumVariantKind, FieldInfo, StructInfo, TypeStructure};
5use quote::ToTokens;
6use std::path::Path;
7use syn::{Attribute, ItemEnum, ItemStruct, Type, Visibility};
8
9/// Parser for Rust structs and enums
10#[derive(Debug)]
11pub struct StructParser {
12    validator_parser: ValidatorParser,
13    serde_parser: SerdeParser,
14}
15
16impl StructParser {
17    pub fn new() -> Self {
18        Self {
19            validator_parser: ValidatorParser::new(),
20            serde_parser: SerdeParser::new(),
21        }
22    }
23
24    /// Check if a struct should be included in type generation
25    pub fn should_include_struct(&self, item_struct: &ItemStruct) -> bool {
26        // Check if struct has Serialize or Deserialize derive
27        for attr in &item_struct.attrs {
28            if self.should_include(attr) {
29                return true;
30            }
31        }
32        false
33    }
34
35    /// Check if an enum should be included in type generation
36    pub fn should_include_enum(&self, item_enum: &ItemEnum) -> bool {
37        // Check if enum has Serialize or Deserialize derive
38        for attr in &item_enum.attrs {
39            if self.should_include(attr) {
40                return true;
41            }
42        }
43        false
44    }
45
46    /// Check if an attribute indicates the type should be included
47    fn should_include(&self, attr: &Attribute) -> bool {
48        let tokens_str = attr.to_token_stream().to_string();
49
50        // Very permissive check for any serde-related derives or attributes
51        // This handles #[derive(Serialize)], #[derive(serde::Serialize)],
52        // #[derive(::serde::Serialize)], and even direct #[serde(...)] attributes
53        tokens_str.contains("Serialize")
54            || tokens_str.contains("Deserialize")
55            || (tokens_str.contains("serde") && !tokens_str.contains("serde_rename"))
56        // Avoid matching internal markers if any
57    }
58
59    /// Parse a Rust struct into StructInfo
60    pub fn parse_struct(
61        &self,
62        item_struct: &ItemStruct,
63        file_path: &Path,
64        type_resolver: &mut TypeResolver,
65    ) -> Option<StructInfo> {
66        // Parse struct-level serde attributes
67        let struct_serde_attrs = self
68            .serde_parser
69            .parse_struct_serde_attrs(&item_struct.attrs);
70
71        let fields = match &item_struct.fields {
72            syn::Fields::Named(fields_named) => fields_named
73                .named
74                .iter()
75                .filter_map(|field| self.parse_field(field, type_resolver))
76                .collect(),
77            syn::Fields::Unnamed(_) => {
78                // Handle tuple structs if needed
79                return None;
80            }
81            syn::Fields::Unit => {
82                // Unit struct
83                Vec::new()
84            }
85        };
86
87        Some(StructInfo {
88            name: item_struct.ident.to_string(),
89            fields,
90            file_path: file_path.to_string_lossy().to_string(),
91            is_enum: false,
92            serde_rename_all: struct_serde_attrs.rename_all,
93            serde_tag: None,
94            enum_variants: None,
95        })
96    }
97
98    /// Parse a Rust enum into StructInfo
99    pub fn parse_enum(
100        &self,
101        item_enum: &ItemEnum,
102        file_path: &Path,
103        type_resolver: &mut TypeResolver,
104    ) -> Option<StructInfo> {
105        // Parse enum-level serde attributes
106        let enum_serde_attrs = self.serde_parser.parse_struct_serde_attrs(&item_enum.attrs);
107
108        // Parse variants into both legacy fields (for backward compatibility) and new enum_variants
109        let mut fields = Vec::new();
110        let mut enum_variants = Vec::new();
111
112        for variant in &item_enum.variants {
113            let mut variant_name = variant.ident.to_string();
114
115            // Strip raw identifier prefix (r#) used for Rust keywords
116            if variant_name.starts_with("r#") {
117                variant_name = variant_name[2..].to_string();
118            }
119
120            // Parse variant-level serde attributes
121            let variant_serde_attrs = self.serde_parser.parse_field_serde_attrs(&variant.attrs);
122
123            match &variant.fields {
124                syn::Fields::Unit => {
125                    // Unit variant: Variant
126                    fields.push(FieldInfo {
127                        name: variant_name.clone(),
128                        rust_type: "enum_variant".to_string(),
129                        is_optional: false,
130                        is_public: true,
131                        validator_attributes: None,
132                        serde_rename: variant_serde_attrs.rename.clone(),
133                        type_structure: TypeStructure::Primitive("string".to_string()),
134                    });
135
136                    enum_variants.push(EnumVariantInfo {
137                        name: variant_name,
138                        kind: EnumVariantKind::Unit,
139                        serde_rename: variant_serde_attrs.rename,
140                    });
141                }
142                syn::Fields::Unnamed(fields_unnamed) => {
143                    // Tuple variant: Variant(T, U)
144                    let tuple_types: Vec<TypeStructure> = fields_unnamed
145                        .unnamed
146                        .iter()
147                        .map(|field| {
148                            let rust_type = Self::type_to_string(&field.ty);
149                            type_resolver.parse_type_structure(&rust_type)
150                        })
151                        .collect();
152
153                    fields.push(FieldInfo {
154                        name: variant_name.clone(),
155                        rust_type: "enum_variant_tuple".to_string(),
156                        is_optional: false,
157                        is_public: true,
158                        validator_attributes: None,
159                        serde_rename: variant_serde_attrs.rename.clone(),
160                        type_structure: TypeStructure::Custom("enum_variant".to_string()),
161                    });
162
163                    enum_variants.push(EnumVariantInfo {
164                        name: variant_name,
165                        kind: EnumVariantKind::Tuple(tuple_types),
166                        serde_rename: variant_serde_attrs.rename,
167                    });
168                }
169                syn::Fields::Named(fields_named) => {
170                    // Struct variant: Variant { field: T }
171                    let struct_fields: Vec<FieldInfo> = fields_named
172                        .named
173                        .iter()
174                        .filter_map(|field| self.parse_field(field, type_resolver))
175                        .collect();
176
177                    fields.push(FieldInfo {
178                        name: variant_name.clone(),
179                        rust_type: "enum_variant_struct".to_string(),
180                        is_optional: false,
181                        is_public: true,
182                        validator_attributes: None,
183                        serde_rename: variant_serde_attrs.rename.clone(),
184                        type_structure: TypeStructure::Custom("enum_variant".to_string()),
185                    });
186
187                    enum_variants.push(EnumVariantInfo {
188                        name: variant_name,
189                        kind: EnumVariantKind::Struct(struct_fields),
190                        serde_rename: variant_serde_attrs.rename,
191                    });
192                }
193            }
194        }
195
196        Some(StructInfo {
197            name: item_enum.ident.to_string(),
198            fields,
199            file_path: file_path.to_string_lossy().to_string(),
200            is_enum: true,
201            serde_rename_all: enum_serde_attrs.rename_all,
202            serde_tag: enum_serde_attrs.tag,
203            enum_variants: Some(enum_variants),
204        })
205    }
206
207    /// Parse a struct field into FieldInfo
208    fn parse_field(
209        &self,
210        field: &syn::Field,
211        type_resolver: &mut TypeResolver,
212    ) -> Option<FieldInfo> {
213        let mut name = field.ident.as_ref()?.to_string();
214
215        // Strip raw identifier prefix (r#) used for Rust keywords
216        if name.starts_with("r#") {
217            name = name[2..].to_string();
218        }
219
220        // Parse field-level serde attributes
221        let field_serde_attrs = self.serde_parser.parse_field_serde_attrs(&field.attrs);
222
223        // Skip fields with #[serde(skip)]
224        if field_serde_attrs.skip {
225            return None;
226        }
227
228        let is_public = matches!(field.vis, Visibility::Public(_));
229        let is_optional = self.is_optional_type(&field.ty);
230        let rust_type = Self::type_to_string(&field.ty);
231        let type_structure = type_resolver.parse_type_structure(&rust_type);
232        let validator_attributes = self
233            .validator_parser
234            .parse_validator_attributes(&field.attrs);
235
236        Some(FieldInfo {
237            name,
238            rust_type,
239            is_optional,
240            is_public,
241            validator_attributes,
242            serde_rename: field_serde_attrs.rename,
243            type_structure,
244        })
245    }
246
247    /// Check if a type is Option<T>
248    fn is_optional_type(&self, ty: &Type) -> bool {
249        if let Type::Path(type_path) = ty {
250            if let Some(segment) = type_path.path.segments.last() {
251                segment.ident == "Option"
252            } else {
253                false
254            }
255        } else {
256            false
257        }
258    }
259
260    /// Convert a Type to its string representation
261    fn type_to_string(ty: &Type) -> String {
262        match ty {
263            Type::Path(type_path) => {
264                let path = &type_path.path;
265                let segments: Vec<String> = path
266                    .segments
267                    .iter()
268                    .map(|segment| {
269                        let ident = segment.ident.to_string();
270                        match &segment.arguments {
271                            syn::PathArguments::None => ident,
272                            syn::PathArguments::AngleBracketed(args) => {
273                                let generic_args: Vec<String> = args
274                                    .args
275                                    .iter()
276                                    .filter_map(|arg| match arg {
277                                        syn::GenericArgument::Type(t) => {
278                                            Some(Self::type_to_string(t))
279                                        }
280                                        _ => None,
281                                    })
282                                    .collect();
283
284                                if generic_args.is_empty() {
285                                    ident
286                                } else {
287                                    format!("{}<{}>", ident, generic_args.join(", "))
288                                }
289                            }
290                            syn::PathArguments::Parenthesized(_) => ident, // Function types, not common in structs
291                        }
292                    })
293                    .collect();
294                segments.join("::")
295            }
296            Type::Reference(type_ref) => {
297                format!("&{}", Self::type_to_string(&type_ref.elem))
298            }
299            Type::Tuple(type_tuple) => {
300                let elements: Vec<String> =
301                    type_tuple.elems.iter().map(Self::type_to_string).collect();
302                format!("({})", elements.join(", "))
303            }
304            Type::Array(type_array) => {
305                format!("[{}; _]", Self::type_to_string(&type_array.elem))
306            }
307            Type::Slice(type_slice) => {
308                format!("[{}]", Self::type_to_string(&type_slice.elem))
309            }
310            _ => "unknown".to_string(),
311        }
312    }
313}
314
315impl Default for StructParser {
316    fn default() -> Self {
317        Self::new()
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324    use serde_rename_rule::RenameRule;
325    use syn::parse_quote;
326
327    // Helper to create a test struct parser
328    fn parser() -> StructParser {
329        StructParser::new()
330    }
331
332    // Helper to create a test type resolver
333    fn type_resolver() -> TypeResolver {
334        TypeResolver::new()
335    }
336
337    mod derive_attribute_detection {
338        use super::*;
339
340        #[test]
341        fn test_should_include_struct_with_serialize() {
342            let parser = parser();
343            let item: ItemStruct = parse_quote! {
344                #[derive(Serialize)]
345                pub struct User {
346                    name: String,
347                }
348            };
349            assert!(parser.should_include_struct(&item));
350        }
351
352        #[test]
353        fn test_should_include_struct_with_deserialize() {
354            let parser = parser();
355            let item: ItemStruct = parse_quote! {
356                #[derive(Deserialize)]
357                pub struct User {
358                    name: String,
359                }
360            };
361            assert!(parser.should_include_struct(&item));
362        }
363
364        #[test]
365        fn test_should_include_struct_with_both() {
366            let parser = parser();
367            let item: ItemStruct = parse_quote! {
368                #[derive(Serialize, Deserialize)]
369                pub struct User {
370                    name: String,
371                }
372            };
373            assert!(parser.should_include_struct(&item));
374        }
375
376        #[test]
377        fn test_should_not_include_struct_without_serde() {
378            let parser = parser();
379            let item: ItemStruct = parse_quote! {
380                #[derive(Debug, Clone)]
381                pub struct User {
382                    name: String,
383                }
384            };
385            assert!(!parser.should_include_struct(&item));
386        }
387
388        #[test]
389        fn test_should_include_enum_with_serialize() {
390            let parser = parser();
391            let item: ItemEnum = parse_quote! {
392                #[derive(Serialize)]
393                pub enum Status {
394                    Active,
395                    Inactive,
396                }
397            };
398            assert!(parser.should_include_enum(&item));
399        }
400
401        #[test]
402        fn test_should_not_include_enum_without_serde() {
403            let parser = parser();
404            let item: ItemEnum = parse_quote! {
405                #[derive(Debug, Clone)]
406                pub enum Status {
407                    Active,
408                    Inactive,
409                }
410            };
411            assert!(!parser.should_include_enum(&item));
412        }
413    }
414
415    mod struct_parsing {
416        use super::*;
417
418        #[test]
419        fn test_parse_simple_struct() {
420            let parser = parser();
421            let mut resolver = type_resolver();
422            let item: ItemStruct = parse_quote! {
423                #[derive(Serialize)]
424                pub struct User {
425                    pub name: String,
426                    pub age: i32,
427                }
428            };
429            let path = Path::new("test.rs");
430            let result = parser.parse_struct(&item, path, &mut resolver);
431
432            assert!(result.is_some());
433            let struct_info = result.unwrap();
434            assert_eq!(struct_info.name, "User");
435            assert_eq!(struct_info.fields.len(), 2);
436            assert!(!struct_info.is_enum);
437            assert_eq!(struct_info.file_path, "test.rs");
438        }
439
440        #[test]
441        fn test_parse_struct_with_optional_fields() {
442            let parser = parser();
443            let mut resolver = type_resolver();
444            let item: ItemStruct = parse_quote! {
445                #[derive(Serialize)]
446                pub struct User {
447                    pub name: String,
448                    pub email: Option<String>,
449                }
450            };
451            let path = Path::new("test.rs");
452            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
453
454            assert_eq!(result.fields.len(), 2);
455            assert!(!result.fields[0].is_optional);
456            assert!(result.fields[1].is_optional);
457        }
458
459        #[test]
460        fn test_parse_struct_with_serde_skip() {
461            let parser = parser();
462            let mut resolver = type_resolver();
463            let item: ItemStruct = parse_quote! {
464                #[derive(Serialize)]
465                pub struct User {
466                    pub name: String,
467                    #[serde(skip)]
468                    pub password: String,
469                }
470            };
471            let path = Path::new("test.rs");
472            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
473
474            // Password field should be skipped
475            assert_eq!(result.fields.len(), 1);
476            assert_eq!(result.fields[0].name, "name");
477        }
478
479        #[test]
480        fn test_parse_struct_with_serde_rename() {
481            let parser = parser();
482            let mut resolver = type_resolver();
483            let item: ItemStruct = parse_quote! {
484                #[derive(Serialize)]
485                pub struct User {
486                    #[serde(rename = "userName")]
487                    pub user_name: String,
488                }
489            };
490            let path = Path::new("test.rs");
491            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
492
493            assert_eq!(result.fields[0].serde_rename, Some("userName".to_string()));
494        }
495
496        #[test]
497        fn test_parse_struct_with_rename_all() {
498            let parser = parser();
499            let mut resolver = type_resolver();
500            let item: ItemStruct = parse_quote! {
501                #[derive(Serialize)]
502                #[serde(rename_all = "camelCase")]
503                pub struct User {
504                    pub user_name: String,
505                }
506            };
507            let path = Path::new("test.rs");
508            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
509
510            assert_eq!(result.serde_rename_all, Some(RenameRule::CamelCase));
511        }
512
513        #[test]
514        fn test_parse_unit_struct() {
515            let parser = parser();
516            let mut resolver = type_resolver();
517            let item: ItemStruct = parse_quote! {
518                #[derive(Serialize)]
519                pub struct Unit;
520            };
521            let path = Path::new("test.rs");
522            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
523
524            assert_eq!(result.name, "Unit");
525            assert_eq!(result.fields.len(), 0);
526        }
527
528        #[test]
529        fn test_parse_tuple_struct_returns_none() {
530            let parser = parser();
531            let mut resolver = type_resolver();
532            let item: ItemStruct = parse_quote! {
533                #[derive(Serialize)]
534                pub struct Point(i32, i32);
535            };
536            let path = Path::new("test.rs");
537            let result = parser.parse_struct(&item, path, &mut resolver);
538
539            // Tuple structs are not supported
540            assert!(result.is_none());
541        }
542
543        #[test]
544        fn test_parse_struct_with_private_fields() {
545            let parser = parser();
546            let mut resolver = type_resolver();
547            let item: ItemStruct = parse_quote! {
548                #[derive(Serialize)]
549                pub struct User {
550                    pub name: String,
551                    age: i32,
552                }
553            };
554            let path = Path::new("test.rs");
555            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
556
557            assert_eq!(result.fields.len(), 2);
558            assert!(result.fields[0].is_public);
559            assert!(!result.fields[1].is_public);
560        }
561    }
562
563    mod enum_parsing {
564        use super::*;
565
566        #[test]
567        fn test_parse_simple_enum() {
568            let parser = parser();
569            let mut resolver = type_resolver();
570            let item: ItemEnum = parse_quote! {
571                #[derive(Serialize)]
572                pub enum Status {
573                    Active,
574                    Inactive,
575                }
576            };
577            let path = Path::new("test.rs");
578            let result = parser.parse_enum(&item, path, &mut resolver);
579
580            assert!(result.is_some());
581            let enum_info = result.unwrap();
582            assert_eq!(enum_info.name, "Status");
583            assert_eq!(enum_info.fields.len(), 2);
584            assert!(enum_info.is_enum);
585        }
586
587        #[test]
588        fn test_parse_enum_unit_variants() {
589            let parser = parser();
590            let mut resolver = type_resolver();
591            let item: ItemEnum = parse_quote! {
592                #[derive(Serialize)]
593                pub enum Status {
594                    Active,
595                    Inactive,
596                    Pending,
597                }
598            };
599            let path = Path::new("test.rs");
600            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
601
602            assert_eq!(result.fields.len(), 3);
603            assert_eq!(result.fields[0].name, "Active");
604            assert_eq!(result.fields[0].rust_type, "enum_variant");
605            assert_eq!(result.fields[1].name, "Inactive");
606            assert_eq!(result.fields[2].name, "Pending");
607
608            // Check enum_variants are populated
609            let variants = result.enum_variants.as_ref().unwrap();
610            assert_eq!(variants.len(), 3);
611            assert!(variants[0].is_unit());
612            assert!(variants[1].is_unit());
613            assert!(variants[2].is_unit());
614        }
615
616        #[test]
617        fn test_parse_enum_tuple_variant() {
618            let parser = parser();
619            let mut resolver = type_resolver();
620            let item: ItemEnum = parse_quote! {
621                #[derive(Serialize)]
622                pub enum Message {
623                    Text(String),
624                    Number(i32),
625                }
626            };
627            let path = Path::new("test.rs");
628            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
629
630            assert_eq!(result.fields.len(), 2);
631            assert_eq!(result.fields[0].rust_type, "enum_variant_tuple");
632            assert_eq!(result.fields[1].rust_type, "enum_variant_tuple");
633
634            // Check enum_variants with tuple types
635            let variants = result.enum_variants.as_ref().unwrap();
636            assert_eq!(variants.len(), 2);
637            assert!(variants[0].is_tuple());
638            assert!(variants[1].is_tuple());
639
640            // Check tuple field types
641            let text_fields = variants[0].tuple_fields().unwrap();
642            assert_eq!(text_fields.len(), 1);
643            assert_eq!(
644                text_fields[0],
645                crate::models::TypeStructure::Primitive("string".to_string())
646            );
647
648            let number_fields = variants[1].tuple_fields().unwrap();
649            assert_eq!(number_fields.len(), 1);
650            assert_eq!(
651                number_fields[0],
652                crate::models::TypeStructure::Primitive("number".to_string())
653            );
654        }
655
656        #[test]
657        fn test_parse_enum_tuple_variant_multiple_fields() {
658            let parser = parser();
659            let mut resolver = type_resolver();
660            let item: ItemEnum = parse_quote! {
661                #[derive(Serialize)]
662                pub enum Message {
663                    Move(i32, i32),
664                    Point(f64, f64, f64),
665                }
666            };
667            let path = Path::new("test.rs");
668            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
669
670            let variants = result.enum_variants.as_ref().unwrap();
671
672            // Check Move variant has 2 number fields
673            let move_fields = variants[0].tuple_fields().unwrap();
674            assert_eq!(move_fields.len(), 2);
675            assert_eq!(
676                move_fields[0],
677                crate::models::TypeStructure::Primitive("number".to_string())
678            );
679            assert_eq!(
680                move_fields[1],
681                crate::models::TypeStructure::Primitive("number".to_string())
682            );
683
684            // Check Point variant has 3 number fields
685            let point_fields = variants[1].tuple_fields().unwrap();
686            assert_eq!(point_fields.len(), 3);
687        }
688
689        #[test]
690        fn test_parse_enum_struct_variant() {
691            let parser = parser();
692            let mut resolver = type_resolver();
693            let item: ItemEnum = parse_quote! {
694                #[derive(Serialize)]
695                pub enum Message {
696                    User { id: i32, name: String },
697                }
698            };
699            let path = Path::new("test.rs");
700            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
701
702            assert_eq!(result.fields.len(), 1);
703            assert_eq!(result.fields[0].rust_type, "enum_variant_struct");
704
705            // Check enum_variants with struct fields
706            let variants = result.enum_variants.as_ref().unwrap();
707            assert_eq!(variants.len(), 1);
708            assert!(variants[0].is_struct());
709
710            let struct_fields = variants[0].struct_fields().unwrap();
711            assert_eq!(struct_fields.len(), 2);
712            assert_eq!(struct_fields[0].name, "id");
713            assert_eq!(struct_fields[0].rust_type, "i32");
714            assert_eq!(struct_fields[1].name, "name");
715            assert_eq!(struct_fields[1].rust_type, "String");
716        }
717
718        #[test]
719        fn test_parse_enum_with_serde_rename_variant() {
720            let parser = parser();
721            let mut resolver = type_resolver();
722            let item: ItemEnum = parse_quote! {
723                #[derive(Serialize)]
724                pub enum Status {
725                    #[serde(rename = "active")]
726                    Active,
727                    #[serde(rename = "inactive")]
728                    Inactive,
729                }
730            };
731            let path = Path::new("test.rs");
732            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
733
734            assert_eq!(result.fields[0].serde_rename, Some("active".to_string()));
735            assert_eq!(result.fields[1].serde_rename, Some("inactive".to_string()));
736
737            // Check enum_variants also have serde_rename
738            let variants = result.enum_variants.as_ref().unwrap();
739            assert_eq!(variants[0].serde_rename, Some("active".to_string()));
740            assert_eq!(variants[1].serde_rename, Some("inactive".to_string()));
741        }
742
743        #[test]
744        fn test_parse_enum_with_rename_all() {
745            let parser = parser();
746            let mut resolver = type_resolver();
747            let item: ItemEnum = parse_quote! {
748                #[derive(Serialize)]
749                #[serde(rename_all = "snake_case")]
750                pub enum Status {
751                    ActiveUser,
752                    InactiveUser,
753                }
754            };
755            let path = Path::new("test.rs");
756            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
757
758            assert_eq!(result.serde_rename_all, Some(RenameRule::SnakeCase));
759        }
760
761        #[test]
762        fn test_parse_enum_with_serde_tag() {
763            let parser = parser();
764            let mut resolver = type_resolver();
765            let item: ItemEnum = parse_quote! {
766                #[derive(Serialize)]
767                #[serde(tag = "type")]
768                pub enum Message {
769                    Text(String),
770                    Quit,
771                }
772            };
773            let path = Path::new("test.rs");
774            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
775
776            assert_eq!(result.serde_tag, Some("type".to_string()));
777        }
778
779        #[test]
780        fn test_parse_enum_with_custom_tag() {
781            let parser = parser();
782            let mut resolver = type_resolver();
783            let item: ItemEnum = parse_quote! {
784                #[derive(Serialize)]
785                #[serde(tag = "kind")]
786                pub enum Action {
787                    Start,
788                    Stop,
789                }
790            };
791            let path = Path::new("test.rs");
792            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
793
794            assert_eq!(result.serde_tag, Some("kind".to_string()));
795            assert_eq!(result.discriminator_tag(), "kind");
796        }
797
798        #[test]
799        fn test_parse_enum_mixed_variants() {
800            let parser = parser();
801            let mut resolver = type_resolver();
802            let item: ItemEnum = parse_quote! {
803                #[derive(Serialize)]
804                #[serde(tag = "type")]
805                pub enum Message {
806                    Quit,
807                    Move(i32, i32),
808                    Write(String),
809                    ChangeColor { r: u8, g: u8, b: u8 },
810                }
811            };
812            let path = Path::new("test.rs");
813            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
814
815            assert_eq!(result.serde_tag, Some("type".to_string()));
816
817            let variants = result.enum_variants.as_ref().unwrap();
818            assert_eq!(variants.len(), 4);
819
820            // Quit is unit
821            assert!(variants[0].is_unit());
822            assert_eq!(variants[0].name, "Quit");
823
824            // Move is tuple with 2 fields
825            assert!(variants[1].is_tuple());
826            assert_eq!(variants[1].name, "Move");
827            assert_eq!(variants[1].tuple_fields().unwrap().len(), 2);
828
829            // Write is tuple with 1 field
830            assert!(variants[2].is_tuple());
831            assert_eq!(variants[2].name, "Write");
832            assert_eq!(variants[2].tuple_fields().unwrap().len(), 1);
833
834            // ChangeColor is struct with 3 fields
835            assert!(variants[3].is_struct());
836            assert_eq!(variants[3].name, "ChangeColor");
837            let struct_fields = variants[3].struct_fields().unwrap();
838            assert_eq!(struct_fields.len(), 3);
839            assert_eq!(struct_fields[0].name, "r");
840            assert_eq!(struct_fields[1].name, "g");
841            assert_eq!(struct_fields[2].name, "b");
842        }
843
844        #[test]
845        fn test_parse_enum_is_simple_vs_complex() {
846            let parser = parser();
847            let mut resolver = type_resolver();
848
849            // Simple enum (all unit variants)
850            let simple_item: ItemEnum = parse_quote! {
851                #[derive(Serialize)]
852                pub enum Status {
853                    Active,
854                    Inactive,
855                }
856            };
857            let path = Path::new("test.rs");
858            let simple_result = parser
859                .parse_enum(&simple_item, path, &mut resolver)
860                .unwrap();
861            assert!(simple_result.is_simple_enum());
862            assert!(!simple_result.is_complex_enum());
863
864            // Complex enum (has tuple variant)
865            let complex_item: ItemEnum = parse_quote! {
866                #[derive(Serialize)]
867                pub enum Message {
868                    Quit,
869                    Text(String),
870                }
871            };
872            let complex_result = parser
873                .parse_enum(&complex_item, path, &mut resolver)
874                .unwrap();
875            assert!(!complex_result.is_simple_enum());
876            assert!(complex_result.is_complex_enum());
877        }
878
879        #[test]
880        fn test_parse_enum_with_nested_types() {
881            let parser = parser();
882            let mut resolver = type_resolver();
883            let item: ItemEnum = parse_quote! {
884                #[derive(Serialize)]
885                pub enum Data {
886                    List(Vec<String>),
887                    Map { items: HashMap<String, i32> },
888                }
889            };
890            let path = Path::new("test.rs");
891            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
892
893            let variants = result.enum_variants.as_ref().unwrap();
894
895            // Check List variant has Vec type
896            let list_fields = variants[0].tuple_fields().unwrap();
897            assert_eq!(list_fields.len(), 1);
898            match &list_fields[0] {
899                crate::models::TypeStructure::Array(inner) => {
900                    assert_eq!(
901                        **inner,
902                        crate::models::TypeStructure::Primitive("string".to_string())
903                    );
904                }
905                _ => panic!("Expected Array type"),
906            }
907
908            // Check Map variant has HashMap field
909            let map_fields = variants[1].struct_fields().unwrap();
910            assert_eq!(map_fields.len(), 1);
911            assert_eq!(map_fields[0].name, "items");
912        }
913    }
914
915    mod type_detection {
916        use super::*;
917
918        #[test]
919        fn test_is_optional_type_with_option() {
920            let parser = parser();
921            let ty: Type = parse_quote!(Option<String>);
922            assert!(parser.is_optional_type(&ty));
923        }
924
925        #[test]
926        fn test_is_optional_type_with_plain_type() {
927            let parser = parser();
928            let ty: Type = parse_quote!(String);
929            assert!(!parser.is_optional_type(&ty));
930        }
931
932        #[test]
933        fn test_is_optional_type_with_nested_option() {
934            let parser = parser();
935            let ty: Type = parse_quote!(Option<Option<String>>);
936            assert!(parser.is_optional_type(&ty));
937        }
938
939        #[test]
940        fn test_is_optional_type_with_vec() {
941            let parser = parser();
942            let ty: Type = parse_quote!(Vec<String>);
943            assert!(!parser.is_optional_type(&ty));
944        }
945    }
946
947    mod type_to_string_conversion {
948        use super::*;
949
950        #[test]
951        fn test_simple_type() {
952            let ty: Type = parse_quote!(String);
953            assert_eq!(StructParser::type_to_string(&ty), "String");
954        }
955
956        #[test]
957        fn test_generic_type() {
958            let ty: Type = parse_quote!(Vec<String>);
959            assert_eq!(StructParser::type_to_string(&ty), "Vec<String>");
960        }
961
962        #[test]
963        fn test_nested_generic_type() {
964            let ty: Type = parse_quote!(Vec<Option<String>>);
965            assert_eq!(StructParser::type_to_string(&ty), "Vec<Option<String>>");
966        }
967
968        #[test]
969        fn test_multiple_generic_args() {
970            let ty: Type = parse_quote!(HashMap<String, i32>);
971            assert_eq!(StructParser::type_to_string(&ty), "HashMap<String, i32>");
972        }
973
974        #[test]
975        fn test_reference_type() {
976            let ty: Type = parse_quote!(&String);
977            assert_eq!(StructParser::type_to_string(&ty), "&String");
978        }
979
980        #[test]
981        fn test_tuple_type() {
982            let ty: Type = parse_quote!((String, i32));
983            assert_eq!(StructParser::type_to_string(&ty), "(String, i32)");
984        }
985
986        #[test]
987        fn test_tuple_three_elements() {
988            let ty: Type = parse_quote!((String, i32, bool));
989            assert_eq!(StructParser::type_to_string(&ty), "(String, i32, bool)");
990        }
991
992        #[test]
993        fn test_array_type() {
994            let ty: Type = parse_quote!([i32; 5]);
995            assert_eq!(StructParser::type_to_string(&ty), "[i32; _]");
996        }
997
998        #[test]
999        fn test_slice_type() {
1000            let ty: Type = parse_quote!([String]);
1001            assert_eq!(StructParser::type_to_string(&ty), "[String]");
1002        }
1003
1004        #[test]
1005        fn test_path_with_segments() {
1006            let ty: Type = parse_quote!(std::collections::HashMap<String, i32>);
1007            assert_eq!(
1008                StructParser::type_to_string(&ty),
1009                "std::collections::HashMap<String, i32>"
1010            );
1011        }
1012
1013        #[test]
1014        fn test_complex_nested_type() {
1015            let ty: Type = parse_quote!(HashMap<String, Vec<Option<User>>>);
1016            assert_eq!(
1017                StructParser::type_to_string(&ty),
1018                "HashMap<String, Vec<Option<User>>>"
1019            );
1020        }
1021    }
1022
1023    mod field_parsing {
1024        use super::*;
1025
1026        #[test]
1027        fn test_parse_field_public() {
1028            let parser = parser();
1029            let mut resolver = type_resolver();
1030            let item: ItemStruct = parse_quote! {
1031                struct Test {
1032                    pub field: String,
1033                }
1034            };
1035            if let syn::Fields::Named(fields) = &item.fields {
1036                let field = fields.named.first().unwrap();
1037                let result = parser.parse_field(field, &mut resolver).unwrap();
1038                assert!(result.is_public);
1039            }
1040        }
1041
1042        #[test]
1043        fn test_parse_field_private() {
1044            let parser = parser();
1045            let mut resolver = type_resolver();
1046            let item: ItemStruct = parse_quote! {
1047                struct Test {
1048                    field: String,
1049                }
1050            };
1051            if let syn::Fields::Named(fields) = &item.fields {
1052                let field = fields.named.first().unwrap();
1053                let result = parser.parse_field(field, &mut resolver).unwrap();
1054                assert!(!result.is_public);
1055            }
1056        }
1057
1058        #[test]
1059        fn test_parse_field_with_serde_skip() {
1060            let parser = parser();
1061            let mut resolver = type_resolver();
1062            let item: ItemStruct = parse_quote! {
1063                struct Test {
1064                    #[serde(skip)]
1065                    field: String,
1066                }
1067            };
1068            if let syn::Fields::Named(fields) = &item.fields {
1069                let field = fields.named.first().unwrap();
1070                let result = parser.parse_field(field, &mut resolver);
1071                assert!(result.is_none());
1072            }
1073        }
1074
1075        #[test]
1076        fn test_parse_field_with_validator() {
1077            let parser = parser();
1078            let mut resolver = type_resolver();
1079            let item: ItemStruct = parse_quote! {
1080                struct Test {
1081                    #[validate(length(min = 1, max = 100))]
1082                    field: String,
1083                }
1084            };
1085            if let syn::Fields::Named(fields) = &item.fields {
1086                let field = fields.named.first().unwrap();
1087                let result = parser.parse_field(field, &mut resolver).unwrap();
1088                assert!(result.validator_attributes.is_some());
1089            }
1090        }
1091    }
1092
1093    mod integration {
1094        use super::*;
1095
1096        #[test]
1097        fn test_parse_full_struct_with_all_features() {
1098            let parser = parser();
1099            let mut resolver = type_resolver();
1100            let item: ItemStruct = parse_quote! {
1101                #[derive(Serialize, Deserialize)]
1102                #[serde(rename_all = "camelCase")]
1103                pub struct User {
1104                    pub id: i32,
1105                    #[serde(rename = "userName")]
1106                    pub user_name: String,
1107                    pub email: Option<String>,
1108                    #[serde(skip)]
1109                    password: String,
1110                    #[validate(length(min = 1, max = 100))]
1111                    pub bio: String,
1112                }
1113            };
1114            let path = Path::new("models.rs");
1115            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
1116
1117            assert_eq!(result.name, "User");
1118            assert_eq!(result.fields.len(), 4); // password skipped
1119            assert_eq!(result.serde_rename_all, Some(RenameRule::CamelCase));
1120            assert_eq!(result.file_path, "models.rs");
1121
1122            // Check specific fields
1123            assert_eq!(result.fields[0].name, "id");
1124            assert_eq!(result.fields[1].name, "user_name");
1125            assert_eq!(result.fields[1].serde_rename, Some("userName".to_string()));
1126            assert!(result.fields[2].is_optional);
1127            assert!(result.fields[3].validator_attributes.is_some());
1128        }
1129
1130        #[test]
1131        fn test_parse_full_enum_with_all_features() {
1132            let parser = parser();
1133            let mut resolver = type_resolver();
1134            let item: ItemEnum = parse_quote! {
1135                #[derive(Serialize, Deserialize)]
1136                #[serde(rename_all = "snake_case", tag = "type")]
1137                pub enum Message {
1138                    #[serde(rename = "simple")]
1139                    Simple,
1140                    Text(String),
1141                    User { id: i32 },
1142                }
1143            };
1144            let path = Path::new("models.rs");
1145            let result = parser.parse_enum(&item, path, &mut resolver).unwrap();
1146
1147            assert_eq!(result.name, "Message");
1148            assert_eq!(result.fields.len(), 3);
1149            assert_eq!(result.serde_rename_all, Some(RenameRule::SnakeCase));
1150            assert!(result.is_enum);
1151            assert_eq!(result.serde_tag, Some("type".to_string()));
1152
1153            // Check variant types (legacy fields)
1154            assert_eq!(result.fields[0].rust_type, "enum_variant");
1155            assert_eq!(result.fields[0].serde_rename, Some("simple".to_string()));
1156            assert_eq!(result.fields[1].rust_type, "enum_variant_tuple");
1157            assert_eq!(result.fields[2].rust_type, "enum_variant_struct");
1158
1159            // Check enum_variants (new format)
1160            let variants = result.enum_variants.as_ref().unwrap();
1161            assert_eq!(variants.len(), 3);
1162            assert!(variants[0].is_unit());
1163            assert!(variants[1].is_tuple());
1164            assert!(variants[2].is_struct());
1165
1166            // Check Text tuple variant fields
1167            let text_fields = variants[1].tuple_fields().unwrap();
1168            assert_eq!(text_fields.len(), 1);
1169
1170            // Check User struct variant fields
1171            let user_fields = variants[2].struct_fields().unwrap();
1172            assert_eq!(user_fields.len(), 1);
1173            assert_eq!(user_fields[0].name, "id");
1174        }
1175    }
1176}