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#[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 pub fn should_include_struct(&self, item_struct: &ItemStruct) -> bool {
26 for attr in &item_struct.attrs {
28 if self.should_include(attr) {
29 return true;
30 }
31 }
32 false
33 }
34
35 pub fn should_include_enum(&self, item_enum: &ItemEnum) -> bool {
37 for attr in &item_enum.attrs {
39 if self.should_include(attr) {
40 return true;
41 }
42 }
43 false
44 }
45
46 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 pub fn parse_struct(
63 &self,
64 item_struct: &ItemStruct,
65 file_path: &Path,
66 type_resolver: &mut TypeResolver,
67 ) -> Option<StructInfo> {
68 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 return None;
82 }
83 syn::Fields::Unit => {
84 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 pub fn parse_enum(
102 &self,
103 item_enum: &ItemEnum,
104 file_path: &Path,
105 type_resolver: &mut TypeResolver,
106 ) -> Option<StructInfo> {
107 let enum_serde_attrs = self.serde_parser.parse_struct_serde_attrs(&item_enum.attrs);
109
110 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 let variant_serde_attrs = self.serde_parser.parse_field_serde_attrs(&variant.attrs);
119
120 match &variant.fields {
121 syn::Fields::Unit => {
122 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 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 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 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 let field_serde_attrs = self.serde_parser.parse_field_serde_attrs(&field.attrs);
214
215 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 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 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, }
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 fn parser() -> StructParser {
314 StructParser::new()
315 }
316
317 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 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 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 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 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 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 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 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 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 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 assert!(variants[0].is_unit());
807 assert_eq!(variants[0].name, "Quit");
808
809 assert!(variants[1].is_tuple());
811 assert_eq!(variants[1].name, "Move");
812 assert_eq!(variants[1].tuple_fields().unwrap().len(), 2);
813
814 assert!(variants[2].is_tuple());
816 assert_eq!(variants[2].name, "Write");
817 assert_eq!(variants[2].tuple_fields().unwrap().len(), 1);
818
819 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 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 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 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 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); assert_eq!(result.serde_rename_all, Some(RenameRule::CamelCase));
1105 assert_eq!(result.file_path, "models.rs");
1106
1107 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 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 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 let text_fields = variants[1].tuple_fields().unwrap();
1153 assert_eq!(text_fields.len(), 1);
1154
1155 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}