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::{FieldInfo, StructInfo};
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        if let Ok(meta_list) = attr.meta.require_list() {
49            if meta_list.path.is_ident("derive") {
50                let tokens_str = meta_list.to_token_stream().to_string();
51
52                tokens_str.contains("Serialize") || tokens_str.contains("Deserialize")
53            } else {
54                false
55            }
56        } else {
57            false
58        }
59    }
60
61    /// Parse a Rust struct into StructInfo
62    pub fn parse_struct(
63        &self,
64        item_struct: &ItemStruct,
65        file_path: &Path,
66        type_resolver: &mut TypeResolver,
67    ) -> Option<StructInfo> {
68        // Parse struct-level serde attributes
69        let struct_serde_attrs = self
70            .serde_parser
71            .parse_struct_serde_attrs(&item_struct.attrs);
72
73        let fields = match &item_struct.fields {
74            syn::Fields::Named(fields_named) => fields_named
75                .named
76                .iter()
77                .filter_map(|field| self.parse_field(field, type_resolver))
78                .collect(),
79            syn::Fields::Unnamed(_) => {
80                // Handle tuple structs if needed
81                return None;
82            }
83            syn::Fields::Unit => {
84                // Unit struct
85                Vec::new()
86            }
87        };
88
89        Some(StructInfo {
90            name: item_struct.ident.to_string(),
91            fields,
92            file_path: file_path.to_string_lossy().to_string(),
93            is_enum: false,
94            serde_rename_all: struct_serde_attrs.rename_all,
95        })
96    }
97
98    /// Parse a Rust enum into StructInfo
99    pub fn parse_enum(&self, item_enum: &ItemEnum, file_path: &Path) -> Option<StructInfo> {
100        // Parse enum-level serde attributes
101        let enum_serde_attrs = self.serde_parser.parse_struct_serde_attrs(&item_enum.attrs);
102
103        let fields = item_enum
104            .variants
105            .iter()
106            .map(|variant| {
107                let variant_name = variant.ident.to_string();
108
109                // Parse variant-level serde attributes
110                let variant_serde_attrs = self.serde_parser.parse_field_serde_attrs(&variant.attrs);
111
112                match &variant.fields {
113                    syn::Fields::Unit => {
114                        // Unit variant: Variant
115                        FieldInfo {
116                            name: variant_name,
117                            rust_type: "enum_variant".to_string(),
118                            is_optional: false,
119                            is_public: true,
120                            validator_attributes: None,
121                            serde_rename: variant_serde_attrs.rename,
122                            type_structure: crate::models::TypeStructure::Primitive(
123                                "string".to_string(),
124                            ),
125                        }
126                    }
127                    syn::Fields::Unnamed(_fields_unnamed) => {
128                        // Tuple variant: Variant(T, U)
129                        // Note: Complex enum variants are not fully supported yet
130                        FieldInfo {
131                            name: variant_name,
132                            rust_type: "enum_variant_tuple".to_string(),
133                            is_optional: false,
134                            is_public: true,
135                            validator_attributes: None,
136                            serde_rename: variant_serde_attrs.rename,
137                            // For enum variants, type structure is not used by generators
138                            type_structure: crate::models::TypeStructure::Custom(
139                                "enum_variant".to_string(),
140                            ),
141                        }
142                    }
143                    syn::Fields::Named(_fields_named) => {
144                        // Struct variant: Variant { field: T }
145                        // Note: Complex enum variants are not fully supported yet
146                        FieldInfo {
147                            name: variant_name,
148                            rust_type: "enum_variant_struct".to_string(),
149                            is_optional: false,
150                            is_public: true,
151                            validator_attributes: None,
152                            serde_rename: variant_serde_attrs.rename,
153                            // For enum variants, type structure is not used by generators
154                            type_structure: crate::models::TypeStructure::Custom(
155                                "enum_variant".to_string(),
156                            ),
157                        }
158                    }
159                }
160            })
161            .collect();
162
163        Some(StructInfo {
164            name: item_enum.ident.to_string(),
165            fields,
166            file_path: file_path.to_string_lossy().to_string(),
167            is_enum: true,
168            serde_rename_all: enum_serde_attrs.rename_all,
169        })
170    }
171
172    /// Parse a struct field into FieldInfo
173    fn parse_field(
174        &self,
175        field: &syn::Field,
176        type_resolver: &mut TypeResolver,
177    ) -> Option<FieldInfo> {
178        let name = field.ident.as_ref()?.to_string();
179
180        // Parse field-level serde attributes
181        let field_serde_attrs = self.serde_parser.parse_field_serde_attrs(&field.attrs);
182
183        // Skip fields with #[serde(skip)]
184        if field_serde_attrs.skip {
185            return None;
186        }
187
188        let is_public = matches!(field.vis, Visibility::Public(_));
189        let is_optional = self.is_optional_type(&field.ty);
190        let rust_type = Self::type_to_string(&field.ty);
191        let type_structure = type_resolver.parse_type_structure(&rust_type);
192        let validator_attributes = self
193            .validator_parser
194            .parse_validator_attributes(&field.attrs);
195
196        Some(FieldInfo {
197            name,
198            rust_type,
199            is_optional,
200            is_public,
201            validator_attributes,
202            serde_rename: field_serde_attrs.rename,
203            type_structure,
204        })
205    }
206
207    /// Check if a type is Option<T>
208    fn is_optional_type(&self, ty: &Type) -> bool {
209        if let Type::Path(type_path) = ty {
210            if let Some(segment) = type_path.path.segments.last() {
211                segment.ident == "Option"
212            } else {
213                false
214            }
215        } else {
216            false
217        }
218    }
219
220    /// Convert a Type to its string representation
221    fn type_to_string(ty: &Type) -> String {
222        match ty {
223            Type::Path(type_path) => {
224                let path = &type_path.path;
225                let segments: Vec<String> = path
226                    .segments
227                    .iter()
228                    .map(|segment| {
229                        let ident = segment.ident.to_string();
230                        match &segment.arguments {
231                            syn::PathArguments::None => ident,
232                            syn::PathArguments::AngleBracketed(args) => {
233                                let generic_args: Vec<String> = args
234                                    .args
235                                    .iter()
236                                    .map(|arg| match arg {
237                                        syn::GenericArgument::Type(t) => Self::type_to_string(t),
238                                        _ => "unknown".to_string(),
239                                    })
240                                    .collect();
241                                format!("{}<{}>", ident, generic_args.join(", "))
242                            }
243                            syn::PathArguments::Parenthesized(_) => ident, // Function types, not common in structs
244                        }
245                    })
246                    .collect();
247                segments.join("::")
248            }
249            Type::Reference(type_ref) => {
250                format!("&{}", Self::type_to_string(&type_ref.elem))
251            }
252            Type::Tuple(type_tuple) => {
253                let elements: Vec<String> =
254                    type_tuple.elems.iter().map(Self::type_to_string).collect();
255                format!("({})", elements.join(", "))
256            }
257            Type::Array(type_array) => {
258                format!("[{}; _]", Self::type_to_string(&type_array.elem))
259            }
260            Type::Slice(type_slice) => {
261                format!("[{}]", Self::type_to_string(&type_slice.elem))
262            }
263            _ => "unknown".to_string(),
264        }
265    }
266}
267
268impl Default for StructParser {
269    fn default() -> Self {
270        Self::new()
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277    use serde_rename_rule::RenameRule;
278    use syn::parse_quote;
279
280    // Helper to create a test struct parser
281    fn parser() -> StructParser {
282        StructParser::new()
283    }
284
285    // Helper to create a test type resolver
286    fn type_resolver() -> TypeResolver {
287        TypeResolver::new()
288    }
289
290    mod derive_attribute_detection {
291        use super::*;
292
293        #[test]
294        fn test_should_include_struct_with_serialize() {
295            let parser = parser();
296            let item: ItemStruct = parse_quote! {
297                #[derive(Serialize)]
298                pub struct User {
299                    name: String,
300                }
301            };
302            assert!(parser.should_include_struct(&item));
303        }
304
305        #[test]
306        fn test_should_include_struct_with_deserialize() {
307            let parser = parser();
308            let item: ItemStruct = parse_quote! {
309                #[derive(Deserialize)]
310                pub struct User {
311                    name: String,
312                }
313            };
314            assert!(parser.should_include_struct(&item));
315        }
316
317        #[test]
318        fn test_should_include_struct_with_both() {
319            let parser = parser();
320            let item: ItemStruct = parse_quote! {
321                #[derive(Serialize, Deserialize)]
322                pub struct User {
323                    name: String,
324                }
325            };
326            assert!(parser.should_include_struct(&item));
327        }
328
329        #[test]
330        fn test_should_not_include_struct_without_serde() {
331            let parser = parser();
332            let item: ItemStruct = parse_quote! {
333                #[derive(Debug, Clone)]
334                pub struct User {
335                    name: String,
336                }
337            };
338            assert!(!parser.should_include_struct(&item));
339        }
340
341        #[test]
342        fn test_should_include_enum_with_serialize() {
343            let parser = parser();
344            let item: ItemEnum = parse_quote! {
345                #[derive(Serialize)]
346                pub enum Status {
347                    Active,
348                    Inactive,
349                }
350            };
351            assert!(parser.should_include_enum(&item));
352        }
353
354        #[test]
355        fn test_should_not_include_enum_without_serde() {
356            let parser = parser();
357            let item: ItemEnum = parse_quote! {
358                #[derive(Debug, Clone)]
359                pub enum Status {
360                    Active,
361                    Inactive,
362                }
363            };
364            assert!(!parser.should_include_enum(&item));
365        }
366    }
367
368    mod struct_parsing {
369        use super::*;
370
371        #[test]
372        fn test_parse_simple_struct() {
373            let parser = parser();
374            let mut resolver = type_resolver();
375            let item: ItemStruct = parse_quote! {
376                #[derive(Serialize)]
377                pub struct User {
378                    pub name: String,
379                    pub age: i32,
380                }
381            };
382            let path = Path::new("test.rs");
383            let result = parser.parse_struct(&item, path, &mut resolver);
384
385            assert!(result.is_some());
386            let struct_info = result.unwrap();
387            assert_eq!(struct_info.name, "User");
388            assert_eq!(struct_info.fields.len(), 2);
389            assert!(!struct_info.is_enum);
390            assert_eq!(struct_info.file_path, "test.rs");
391        }
392
393        #[test]
394        fn test_parse_struct_with_optional_fields() {
395            let parser = parser();
396            let mut resolver = type_resolver();
397            let item: ItemStruct = parse_quote! {
398                #[derive(Serialize)]
399                pub struct User {
400                    pub name: String,
401                    pub email: Option<String>,
402                }
403            };
404            let path = Path::new("test.rs");
405            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
406
407            assert_eq!(result.fields.len(), 2);
408            assert!(!result.fields[0].is_optional);
409            assert!(result.fields[1].is_optional);
410        }
411
412        #[test]
413        fn test_parse_struct_with_serde_skip() {
414            let parser = parser();
415            let mut resolver = type_resolver();
416            let item: ItemStruct = parse_quote! {
417                #[derive(Serialize)]
418                pub struct User {
419                    pub name: String,
420                    #[serde(skip)]
421                    pub password: String,
422                }
423            };
424            let path = Path::new("test.rs");
425            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
426
427            // Password field should be skipped
428            assert_eq!(result.fields.len(), 1);
429            assert_eq!(result.fields[0].name, "name");
430        }
431
432        #[test]
433        fn test_parse_struct_with_serde_rename() {
434            let parser = parser();
435            let mut resolver = type_resolver();
436            let item: ItemStruct = parse_quote! {
437                #[derive(Serialize)]
438                pub struct User {
439                    #[serde(rename = "userName")]
440                    pub user_name: String,
441                }
442            };
443            let path = Path::new("test.rs");
444            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
445
446            assert_eq!(result.fields[0].serde_rename, Some("userName".to_string()));
447        }
448
449        #[test]
450        fn test_parse_struct_with_rename_all() {
451            let parser = parser();
452            let mut resolver = type_resolver();
453            let item: ItemStruct = parse_quote! {
454                #[derive(Serialize)]
455                #[serde(rename_all = "camelCase")]
456                pub struct User {
457                    pub user_name: String,
458                }
459            };
460            let path = Path::new("test.rs");
461            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
462
463            assert_eq!(result.serde_rename_all, Some(RenameRule::CamelCase));
464        }
465
466        #[test]
467        fn test_parse_unit_struct() {
468            let parser = parser();
469            let mut resolver = type_resolver();
470            let item: ItemStruct = parse_quote! {
471                #[derive(Serialize)]
472                pub struct Unit;
473            };
474            let path = Path::new("test.rs");
475            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
476
477            assert_eq!(result.name, "Unit");
478            assert_eq!(result.fields.len(), 0);
479        }
480
481        #[test]
482        fn test_parse_tuple_struct_returns_none() {
483            let parser = parser();
484            let mut resolver = type_resolver();
485            let item: ItemStruct = parse_quote! {
486                #[derive(Serialize)]
487                pub struct Point(i32, i32);
488            };
489            let path = Path::new("test.rs");
490            let result = parser.parse_struct(&item, path, &mut resolver);
491
492            // Tuple structs are not supported
493            assert!(result.is_none());
494        }
495
496        #[test]
497        fn test_parse_struct_with_private_fields() {
498            let parser = parser();
499            let mut resolver = type_resolver();
500            let item: ItemStruct = parse_quote! {
501                #[derive(Serialize)]
502                pub struct User {
503                    pub name: String,
504                    age: i32,
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.fields.len(), 2);
511            assert!(result.fields[0].is_public);
512            assert!(!result.fields[1].is_public);
513        }
514    }
515
516    mod enum_parsing {
517        use super::*;
518
519        #[test]
520        fn test_parse_simple_enum() {
521            let parser = parser();
522            let item: ItemEnum = parse_quote! {
523                #[derive(Serialize)]
524                pub enum Status {
525                    Active,
526                    Inactive,
527                }
528            };
529            let path = Path::new("test.rs");
530            let result = parser.parse_enum(&item, path);
531
532            assert!(result.is_some());
533            let enum_info = result.unwrap();
534            assert_eq!(enum_info.name, "Status");
535            assert_eq!(enum_info.fields.len(), 2);
536            assert!(enum_info.is_enum);
537        }
538
539        #[test]
540        fn test_parse_enum_unit_variants() {
541            let parser = parser();
542            let item: ItemEnum = parse_quote! {
543                #[derive(Serialize)]
544                pub enum Status {
545                    Active,
546                    Inactive,
547                    Pending,
548                }
549            };
550            let path = Path::new("test.rs");
551            let result = parser.parse_enum(&item, path).unwrap();
552
553            assert_eq!(result.fields.len(), 3);
554            assert_eq!(result.fields[0].name, "Active");
555            assert_eq!(result.fields[0].rust_type, "enum_variant");
556            assert_eq!(result.fields[1].name, "Inactive");
557            assert_eq!(result.fields[2].name, "Pending");
558        }
559
560        #[test]
561        fn test_parse_enum_tuple_variant() {
562            let parser = parser();
563            let item: ItemEnum = parse_quote! {
564                #[derive(Serialize)]
565                pub enum Message {
566                    Text(String),
567                    Number(i32),
568                }
569            };
570            let path = Path::new("test.rs");
571            let result = parser.parse_enum(&item, path).unwrap();
572
573            assert_eq!(result.fields.len(), 2);
574            assert_eq!(result.fields[0].rust_type, "enum_variant_tuple");
575            assert_eq!(result.fields[1].rust_type, "enum_variant_tuple");
576        }
577
578        #[test]
579        fn test_parse_enum_struct_variant() {
580            let parser = parser();
581            let item: ItemEnum = parse_quote! {
582                #[derive(Serialize)]
583                pub enum Message {
584                    User { id: i32, name: String },
585                }
586            };
587            let path = Path::new("test.rs");
588            let result = parser.parse_enum(&item, path).unwrap();
589
590            assert_eq!(result.fields.len(), 1);
591            assert_eq!(result.fields[0].rust_type, "enum_variant_struct");
592        }
593
594        #[test]
595        fn test_parse_enum_with_serde_rename_variant() {
596            let parser = parser();
597            let item: ItemEnum = parse_quote! {
598                #[derive(Serialize)]
599                pub enum Status {
600                    #[serde(rename = "active")]
601                    Active,
602                    #[serde(rename = "inactive")]
603                    Inactive,
604                }
605            };
606            let path = Path::new("test.rs");
607            let result = parser.parse_enum(&item, path).unwrap();
608
609            assert_eq!(result.fields[0].serde_rename, Some("active".to_string()));
610            assert_eq!(result.fields[1].serde_rename, Some("inactive".to_string()));
611        }
612
613        #[test]
614        fn test_parse_enum_with_rename_all() {
615            let parser = parser();
616            let item: ItemEnum = parse_quote! {
617                #[derive(Serialize)]
618                #[serde(rename_all = "snake_case")]
619                pub enum Status {
620                    ActiveUser,
621                    InactiveUser,
622                }
623            };
624            let path = Path::new("test.rs");
625            let result = parser.parse_enum(&item, path).unwrap();
626
627            assert_eq!(result.serde_rename_all, Some(RenameRule::SnakeCase));
628        }
629    }
630
631    mod type_detection {
632        use super::*;
633
634        #[test]
635        fn test_is_optional_type_with_option() {
636            let parser = parser();
637            let ty: Type = parse_quote!(Option<String>);
638            assert!(parser.is_optional_type(&ty));
639        }
640
641        #[test]
642        fn test_is_optional_type_with_plain_type() {
643            let parser = parser();
644            let ty: Type = parse_quote!(String);
645            assert!(!parser.is_optional_type(&ty));
646        }
647
648        #[test]
649        fn test_is_optional_type_with_nested_option() {
650            let parser = parser();
651            let ty: Type = parse_quote!(Option<Option<String>>);
652            assert!(parser.is_optional_type(&ty));
653        }
654
655        #[test]
656        fn test_is_optional_type_with_vec() {
657            let parser = parser();
658            let ty: Type = parse_quote!(Vec<String>);
659            assert!(!parser.is_optional_type(&ty));
660        }
661    }
662
663    mod type_to_string_conversion {
664        use super::*;
665
666        #[test]
667        fn test_simple_type() {
668            let ty: Type = parse_quote!(String);
669            assert_eq!(StructParser::type_to_string(&ty), "String");
670        }
671
672        #[test]
673        fn test_generic_type() {
674            let ty: Type = parse_quote!(Vec<String>);
675            assert_eq!(StructParser::type_to_string(&ty), "Vec<String>");
676        }
677
678        #[test]
679        fn test_nested_generic_type() {
680            let ty: Type = parse_quote!(Vec<Option<String>>);
681            assert_eq!(StructParser::type_to_string(&ty), "Vec<Option<String>>");
682        }
683
684        #[test]
685        fn test_multiple_generic_args() {
686            let ty: Type = parse_quote!(HashMap<String, i32>);
687            assert_eq!(StructParser::type_to_string(&ty), "HashMap<String, i32>");
688        }
689
690        #[test]
691        fn test_reference_type() {
692            let ty: Type = parse_quote!(&String);
693            assert_eq!(StructParser::type_to_string(&ty), "&String");
694        }
695
696        #[test]
697        fn test_tuple_type() {
698            let ty: Type = parse_quote!((String, i32));
699            assert_eq!(StructParser::type_to_string(&ty), "(String, i32)");
700        }
701
702        #[test]
703        fn test_tuple_three_elements() {
704            let ty: Type = parse_quote!((String, i32, bool));
705            assert_eq!(StructParser::type_to_string(&ty), "(String, i32, bool)");
706        }
707
708        #[test]
709        fn test_array_type() {
710            let ty: Type = parse_quote!([i32; 5]);
711            assert_eq!(StructParser::type_to_string(&ty), "[i32; _]");
712        }
713
714        #[test]
715        fn test_slice_type() {
716            let ty: Type = parse_quote!([String]);
717            assert_eq!(StructParser::type_to_string(&ty), "[String]");
718        }
719
720        #[test]
721        fn test_path_with_segments() {
722            let ty: Type = parse_quote!(std::collections::HashMap<String, i32>);
723            assert_eq!(
724                StructParser::type_to_string(&ty),
725                "std::collections::HashMap<String, i32>"
726            );
727        }
728
729        #[test]
730        fn test_complex_nested_type() {
731            let ty: Type = parse_quote!(HashMap<String, Vec<Option<User>>>);
732            assert_eq!(
733                StructParser::type_to_string(&ty),
734                "HashMap<String, Vec<Option<User>>>"
735            );
736        }
737    }
738
739    mod field_parsing {
740        use super::*;
741
742        #[test]
743        fn test_parse_field_public() {
744            let parser = parser();
745            let mut resolver = type_resolver();
746            let item: ItemStruct = parse_quote! {
747                struct Test {
748                    pub field: String,
749                }
750            };
751            if let syn::Fields::Named(fields) = &item.fields {
752                let field = fields.named.first().unwrap();
753                let result = parser.parse_field(field, &mut resolver).unwrap();
754                assert!(result.is_public);
755            }
756        }
757
758        #[test]
759        fn test_parse_field_private() {
760            let parser = parser();
761            let mut resolver = type_resolver();
762            let item: ItemStruct = parse_quote! {
763                struct Test {
764                    field: String,
765                }
766            };
767            if let syn::Fields::Named(fields) = &item.fields {
768                let field = fields.named.first().unwrap();
769                let result = parser.parse_field(field, &mut resolver).unwrap();
770                assert!(!result.is_public);
771            }
772        }
773
774        #[test]
775        fn test_parse_field_with_serde_skip() {
776            let parser = parser();
777            let mut resolver = type_resolver();
778            let item: ItemStruct = parse_quote! {
779                struct Test {
780                    #[serde(skip)]
781                    field: String,
782                }
783            };
784            if let syn::Fields::Named(fields) = &item.fields {
785                let field = fields.named.first().unwrap();
786                let result = parser.parse_field(field, &mut resolver);
787                assert!(result.is_none());
788            }
789        }
790
791        #[test]
792        fn test_parse_field_with_validator() {
793            let parser = parser();
794            let mut resolver = type_resolver();
795            let item: ItemStruct = parse_quote! {
796                struct Test {
797                    #[validate(length(min = 1, max = 100))]
798                    field: String,
799                }
800            };
801            if let syn::Fields::Named(fields) = &item.fields {
802                let field = fields.named.first().unwrap();
803                let result = parser.parse_field(field, &mut resolver).unwrap();
804                assert!(result.validator_attributes.is_some());
805            }
806        }
807    }
808
809    mod integration {
810        use super::*;
811
812        #[test]
813        fn test_parse_full_struct_with_all_features() {
814            let parser = parser();
815            let mut resolver = type_resolver();
816            let item: ItemStruct = parse_quote! {
817                #[derive(Serialize, Deserialize)]
818                #[serde(rename_all = "camelCase")]
819                pub struct User {
820                    pub id: i32,
821                    #[serde(rename = "userName")]
822                    pub user_name: String,
823                    pub email: Option<String>,
824                    #[serde(skip)]
825                    password: String,
826                    #[validate(length(min = 1, max = 100))]
827                    pub bio: String,
828                }
829            };
830            let path = Path::new("models.rs");
831            let result = parser.parse_struct(&item, path, &mut resolver).unwrap();
832
833            assert_eq!(result.name, "User");
834            assert_eq!(result.fields.len(), 4); // password skipped
835            assert_eq!(result.serde_rename_all, Some(RenameRule::CamelCase));
836            assert_eq!(result.file_path, "models.rs");
837
838            // Check specific fields
839            assert_eq!(result.fields[0].name, "id");
840            assert_eq!(result.fields[1].name, "user_name");
841            assert_eq!(result.fields[1].serde_rename, Some("userName".to_string()));
842            assert!(result.fields[2].is_optional);
843            assert!(result.fields[3].validator_attributes.is_some());
844        }
845
846        #[test]
847        fn test_parse_full_enum_with_all_features() {
848            let parser = parser();
849            let item: ItemEnum = parse_quote! {
850                #[derive(Serialize, Deserialize)]
851                #[serde(rename_all = "snake_case")]
852                pub enum Message {
853                    #[serde(rename = "simple")]
854                    Simple,
855                    Text(String),
856                    User { id: i32 },
857                }
858            };
859            let path = Path::new("models.rs");
860            let result = parser.parse_enum(&item, path).unwrap();
861
862            assert_eq!(result.name, "Message");
863            assert_eq!(result.fields.len(), 3);
864            assert_eq!(result.serde_rename_all, Some(RenameRule::SnakeCase));
865            assert!(result.is_enum);
866
867            // Check variant types
868            assert_eq!(result.fields[0].rust_type, "enum_variant");
869            assert_eq!(result.fields[0].serde_rename, Some("simple".to_string()));
870            assert_eq!(result.fields[1].rust_type, "enum_variant_tuple");
871            assert_eq!(result.fields[2].rust_type, "enum_variant_struct");
872        }
873    }
874}