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