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