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