1use serde::{Deserialize, Serialize};
2use serde_rename_rule::RenameRule;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7#[serde(rename_all = "camelCase")]
8pub enum TypeStructure {
9 Primitive(String),
11
12 Array(Box<TypeStructure>),
14
15 Map {
17 key: Box<TypeStructure>,
18 value: Box<TypeStructure>,
19 },
20
21 Set(Box<TypeStructure>),
23
24 Tuple(Vec<TypeStructure>),
26
27 Optional(Box<TypeStructure>),
29
30 Result(Box<TypeStructure>),
32
33 Custom(String),
35}
36
37impl Default for TypeStructure {
38 fn default() -> Self {
39 TypeStructure::Primitive("string".to_string())
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
46#[serde(rename_all = "camelCase")]
47pub enum EnumVariantKind {
48 Unit,
50 Tuple(Vec<TypeStructure>),
52 Struct(Vec<FieldInfo>),
54}
55
56#[derive(Debug, Clone)]
58pub struct EnumVariantInfo {
59 pub name: String,
61 pub kind: EnumVariantKind,
63 pub serde_rename: Option<String>,
65}
66
67impl EnumVariantInfo {
68 pub fn is_unit(&self) -> bool {
70 matches!(self.kind, EnumVariantKind::Unit)
71 }
72
73 pub fn is_tuple(&self) -> bool {
75 matches!(self.kind, EnumVariantKind::Tuple(_))
76 }
77
78 pub fn is_struct(&self) -> bool {
80 matches!(self.kind, EnumVariantKind::Struct(_))
81 }
82
83 pub fn tuple_fields(&self) -> Option<&Vec<TypeStructure>> {
85 match &self.kind {
86 EnumVariantKind::Tuple(fields) => Some(fields),
87 _ => None,
88 }
89 }
90
91 pub fn struct_fields(&self) -> Option<&Vec<FieldInfo>> {
93 match &self.kind {
94 EnumVariantKind::Struct(fields) => Some(fields),
95 _ => None,
96 }
97 }
98}
99
100pub struct CommandInfo {
101 pub name: String,
102 pub file_path: String,
103 pub line_number: usize,
104 pub parameters: Vec<ParameterInfo>,
105 pub return_type: String, pub return_type_structure: TypeStructure,
108 pub is_async: bool,
109 pub channels: Vec<ChannelInfo>,
110 pub serde_rename_all: Option<RenameRule>,
113}
114
115impl CommandInfo {
116 #[doc(hidden)]
118 pub fn new_for_test(
119 name: impl Into<String>,
120 file_path: impl Into<String>,
121 line_number: usize,
122 parameters: Vec<ParameterInfo>,
123 return_type: impl Into<String>,
124 is_async: bool,
125 channels: Vec<ChannelInfo>,
126 ) -> Self {
127 use crate::analysis::type_resolver::TypeResolver;
128 let return_type_str = return_type.into();
129 let type_resolver = TypeResolver::new();
130 let return_type_structure = type_resolver.parse_type_structure(&return_type_str);
131
132 Self {
133 name: name.into(),
134 file_path: file_path.into(),
135 line_number,
136 parameters,
137 return_type: return_type_str,
138 return_type_structure,
139 is_async,
140 channels,
141 serde_rename_all: None,
142 }
143 }
144}
145
146pub struct ParameterInfo {
147 pub name: String,
148 pub rust_type: String,
149 pub is_optional: bool,
150 pub type_structure: TypeStructure,
152 pub serde_rename: Option<String>,
155}
156
157#[derive(Clone, Debug)]
158pub struct StructInfo {
159 pub name: String,
160 pub fields: Vec<FieldInfo>,
161 pub file_path: String,
162 pub is_enum: bool,
163 pub serde_rename_all: Option<RenameRule>,
165 pub serde_tag: Option<String>,
168 pub enum_variants: Option<Vec<EnumVariantInfo>>,
171}
172
173impl StructInfo {
174 pub fn is_simple_enum(&self) -> bool {
177 if !self.is_enum {
178 return false;
179 }
180
181 match &self.enum_variants {
182 Some(variants) => variants.iter().all(|v| v.is_unit()),
183 None => self.fields.iter().all(|f| f.rust_type == "enum_variant"),
185 }
186 }
187
188 pub fn is_complex_enum(&self) -> bool {
191 self.is_enum && !self.is_simple_enum()
192 }
193
194 pub fn discriminator_tag(&self) -> &str {
197 self.serde_tag.as_deref().unwrap_or("type")
198 }
199}
200
201#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
202#[serde(rename_all = "camelCase")]
203pub struct FieldInfo {
204 pub name: String,
205 pub rust_type: String,
206 pub is_optional: bool,
207 pub is_public: bool,
208 pub validator_attributes: Option<ValidatorAttributes>,
209 pub serde_rename: Option<String>,
211 pub type_structure: TypeStructure,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
216#[serde(rename_all = "camelCase")]
217pub struct ValidatorAttributes {
218 pub length: Option<LengthConstraint>,
219 pub range: Option<RangeConstraint>,
220 pub email: bool,
221 pub url: bool,
222 pub custom_message: Option<String>,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
226#[serde(rename_all = "camelCase")]
227pub struct LengthConstraint {
228 pub min: Option<u64>,
229 pub max: Option<u64>,
230 pub message: Option<String>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
234#[serde(rename_all = "camelCase")]
235pub struct RangeConstraint {
236 pub min: Option<f64>,
237 pub max: Option<f64>,
238 pub message: Option<String>,
239}
240
241pub struct EventInfo {
243 pub event_name: String,
244 pub payload_type: String,
245 pub payload_type_structure: TypeStructure,
247 pub file_path: String,
248 pub line_number: usize,
249}
250
251#[derive(Clone)]
253pub struct ChannelInfo {
254 pub parameter_name: String,
255 pub message_type: String,
256 pub command_name: String,
257 pub file_path: String,
258 pub line_number: usize,
259 pub serde_rename: Option<String>,
262 pub message_type_structure: TypeStructure,
264}
265
266impl ChannelInfo {
267 #[doc(hidden)]
269 pub fn new_for_test(
270 parameter_name: impl Into<String>,
271 message_type: impl Into<String>,
272 command_name: impl Into<String>,
273 file_path: impl Into<String>,
274 line_number: usize,
275 ) -> Self {
276 let message_type_str = message_type.into();
277 Self {
278 parameter_name: parameter_name.into(),
279 message_type: message_type_str.clone(),
280 command_name: command_name.into(),
281 file_path: file_path.into(),
282 line_number,
283 serde_rename: None,
284 message_type_structure: crate::analysis::type_resolver::TypeResolver::new()
286 .parse_type_structure(&message_type_str),
287 }
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 mod type_structure {
297 use super::*;
298
299 #[test]
300 fn test_default_is_string_primitive() {
301 let default_type = TypeStructure::default();
302 match default_type {
303 TypeStructure::Primitive(name) => assert_eq!(name, "string"),
304 _ => panic!("Default should be Primitive(\"string\")"),
305 }
306 }
307
308 #[test]
309 fn test_primitive_variants() {
310 let types = vec!["string", "number", "boolean", "void"];
311 for type_name in types {
312 let primitive = TypeStructure::Primitive(type_name.to_string());
313 match primitive {
314 TypeStructure::Primitive(name) => assert_eq!(name, type_name),
315 _ => panic!("Should be Primitive variant"),
316 }
317 }
318 }
319
320 #[test]
321 fn test_array_wraps_inner_type() {
322 let inner = TypeStructure::Primitive("number".to_string());
323 let array = TypeStructure::Array(Box::new(inner));
324
325 match array {
326 TypeStructure::Array(boxed) => match *boxed {
327 TypeStructure::Primitive(name) => assert_eq!(name, "number"),
328 _ => panic!("Inner should be Primitive"),
329 },
330 _ => panic!("Should be Array variant"),
331 }
332 }
333
334 #[test]
335 fn test_map_has_key_and_value() {
336 let key = TypeStructure::Primitive("string".to_string());
337 let value = TypeStructure::Primitive("number".to_string());
338 let map = TypeStructure::Map {
339 key: Box::new(key),
340 value: Box::new(value),
341 };
342
343 match map {
344 TypeStructure::Map { key, value } => match (*key, *value) {
345 (TypeStructure::Primitive(k), TypeStructure::Primitive(v)) => {
346 assert_eq!(k, "string");
347 assert_eq!(v, "number");
348 }
349 _ => panic!("Key and value should be Primitives"),
350 },
351 _ => panic!("Should be Map variant"),
352 }
353 }
354
355 #[test]
356 fn test_set_wraps_inner_type() {
357 let inner = TypeStructure::Primitive("string".to_string());
358 let set = TypeStructure::Set(Box::new(inner));
359
360 match set {
361 TypeStructure::Set(boxed) => match *boxed {
362 TypeStructure::Primitive(name) => assert_eq!(name, "string"),
363 _ => panic!("Inner should be Primitive"),
364 },
365 _ => panic!("Should be Set variant"),
366 }
367 }
368
369 #[test]
370 fn test_tuple_with_multiple_types() {
371 let types = vec![
372 TypeStructure::Primitive("string".to_string()),
373 TypeStructure::Primitive("number".to_string()),
374 TypeStructure::Primitive("boolean".to_string()),
375 ];
376 let tuple = TypeStructure::Tuple(types);
377
378 match tuple {
379 TypeStructure::Tuple(types) => {
380 assert_eq!(types.len(), 3);
381 match &types[0] {
382 TypeStructure::Primitive(name) => assert_eq!(name, "string"),
383 _ => panic!("First type should be string"),
384 }
385 }
386 _ => panic!("Should be Tuple variant"),
387 }
388 }
389
390 #[test]
391 fn test_empty_tuple() {
392 let tuple = TypeStructure::Tuple(vec![]);
393 match tuple {
394 TypeStructure::Tuple(types) => assert_eq!(types.len(), 0),
395 _ => panic!("Should be Tuple variant"),
396 }
397 }
398
399 #[test]
400 fn test_optional_wraps_inner_type() {
401 let inner = TypeStructure::Custom("User".to_string());
402 let optional = TypeStructure::Optional(Box::new(inner));
403
404 match optional {
405 TypeStructure::Optional(boxed) => match *boxed {
406 TypeStructure::Custom(name) => assert_eq!(name, "User"),
407 _ => panic!("Inner should be Custom"),
408 },
409 _ => panic!("Should be Optional variant"),
410 }
411 }
412
413 #[test]
414 fn test_result_wraps_success_type() {
415 let success = TypeStructure::Primitive("string".to_string());
416 let result = TypeStructure::Result(Box::new(success));
417
418 match result {
419 TypeStructure::Result(boxed) => match *boxed {
420 TypeStructure::Primitive(name) => assert_eq!(name, "string"),
421 _ => panic!("Inner should be Primitive"),
422 },
423 _ => panic!("Should be Result variant"),
424 }
425 }
426
427 #[test]
428 fn test_custom_type() {
429 let custom = TypeStructure::Custom("UserProfile".to_string());
430 match custom {
431 TypeStructure::Custom(name) => assert_eq!(name, "UserProfile"),
432 _ => panic!("Should be Custom variant"),
433 }
434 }
435
436 #[test]
437 fn test_nested_structures() {
438 let user = TypeStructure::Custom("User".to_string());
440 let map = TypeStructure::Map {
441 key: Box::new(TypeStructure::Primitive("string".to_string())),
442 value: Box::new(user),
443 };
444 let optional = TypeStructure::Optional(Box::new(map));
445 let array = TypeStructure::Array(Box::new(optional));
446
447 match array {
448 TypeStructure::Array(arr_inner) => match *arr_inner {
449 TypeStructure::Optional(opt_inner) => match *opt_inner {
450 TypeStructure::Map { key, value } => match (*key, *value) {
451 (TypeStructure::Primitive(k), TypeStructure::Custom(v)) => {
452 assert_eq!(k, "string");
453 assert_eq!(v, "User");
454 }
455 _ => panic!("Map types incorrect"),
456 },
457 _ => panic!("Should be Map"),
458 },
459 _ => panic!("Should be Optional"),
460 },
461 _ => panic!("Should be Array"),
462 }
463 }
464
465 #[test]
466 fn test_clone_type_structure() {
467 let original = TypeStructure::Primitive("string".to_string());
468 let cloned = original.clone();
469
470 match (original, cloned) {
471 (TypeStructure::Primitive(o), TypeStructure::Primitive(c)) => {
472 assert_eq!(o, c);
473 }
474 _ => panic!("Clone should maintain variant"),
475 }
476 }
477
478 #[test]
479 fn test_serialize_deserialize_primitive() {
480 let primitive = TypeStructure::Primitive("number".to_string());
481 let json = serde_json::to_string(&primitive).unwrap();
482 let deserialized: TypeStructure = serde_json::from_str(&json).unwrap();
483
484 match deserialized {
485 TypeStructure::Primitive(name) => assert_eq!(name, "number"),
486 _ => panic!("Should deserialize to Primitive"),
487 }
488 }
489
490 #[test]
491 fn test_serialize_deserialize_complex() {
492 let complex = TypeStructure::Array(Box::new(TypeStructure::Optional(Box::new(
493 TypeStructure::Custom("User".to_string()),
494 ))));
495
496 let json = serde_json::to_string(&complex).unwrap();
497 let deserialized: TypeStructure = serde_json::from_str(&json).unwrap();
498
499 match deserialized {
500 TypeStructure::Array(arr) => match *arr {
501 TypeStructure::Optional(opt) => match *opt {
502 TypeStructure::Custom(name) => assert_eq!(name, "User"),
503 _ => panic!("Should be Custom"),
504 },
505 _ => panic!("Should be Optional"),
506 },
507 _ => panic!("Should be Array"),
508 }
509 }
510 }
511
512 mod validator_attributes {
514 use super::*;
515
516 #[test]
517 fn test_length_constraint() {
518 let length = LengthConstraint {
519 min: Some(5),
520 max: Some(100),
521 message: Some("Invalid length".to_string()),
522 };
523
524 assert_eq!(length.min, Some(5));
525 assert_eq!(length.max, Some(100));
526 assert_eq!(length.message, Some("Invalid length".to_string()));
527 }
528
529 #[test]
530 fn test_range_constraint() {
531 let range = RangeConstraint {
532 min: Some(0.0),
533 max: Some(10.5),
534 message: Some("Out of range".to_string()),
535 };
536
537 assert_eq!(range.min, Some(0.0));
538 assert_eq!(range.max, Some(10.5));
539 assert_eq!(range.message, Some("Out of range".to_string()));
540 }
541
542 #[test]
543 fn test_validator_attributes_email() {
544 let validator = ValidatorAttributes {
545 length: None,
546 range: None,
547 email: true,
548 url: false,
549 custom_message: None,
550 };
551
552 assert!(validator.email);
553 assert!(!validator.url);
554 }
555
556 #[test]
557 fn test_validator_attributes_with_length() {
558 let validator = ValidatorAttributes {
559 length: Some(LengthConstraint {
560 min: Some(1),
561 max: Some(50),
562 message: None,
563 }),
564 range: None,
565 email: false,
566 url: false,
567 custom_message: None,
568 };
569
570 assert!(validator.length.is_some());
571 let length = validator.length.unwrap();
572 assert_eq!(length.min, Some(1));
573 assert_eq!(length.max, Some(50));
574 }
575
576 #[test]
577 fn test_serialize_validator_attributes() {
578 let validator = ValidatorAttributes {
579 length: Some(LengthConstraint {
580 min: Some(5),
581 max: Some(100),
582 message: None,
583 }),
584 range: None,
585 email: true,
586 url: false,
587 custom_message: Some("Custom error".to_string()),
588 };
589
590 let json = serde_json::to_string(&validator).unwrap();
591 let deserialized: ValidatorAttributes = serde_json::from_str(&json).unwrap();
592
593 assert!(deserialized.email);
594 assert_eq!(
595 deserialized.custom_message,
596 Some("Custom error".to_string())
597 );
598 assert!(deserialized.length.is_some());
599 }
600
601 #[test]
602 fn test_validator_attributes_clone() {
603 let original = ValidatorAttributes {
604 length: None,
605 range: Some(RangeConstraint {
606 min: Some(0.0),
607 max: Some(1.0),
608 message: None,
609 }),
610 email: false,
611 url: true,
612 custom_message: None,
613 };
614
615 let cloned = original.clone();
616 assert!(cloned.url);
617 assert!(cloned.range.is_some());
618 }
619 }
620
621 mod command_info {
623 use super::*;
624
625 #[test]
626 fn test_new_for_test_creates_valid_command() {
627 let params = vec![];
628 let channels = vec![];
629
630 let cmd = CommandInfo::new_for_test(
631 "greet",
632 "src/main.rs",
633 10,
634 params,
635 "String",
636 false,
637 channels,
638 );
639
640 assert_eq!(cmd.name, "greet");
641 assert_eq!(cmd.file_path, "src/main.rs");
642 assert_eq!(cmd.line_number, 10);
643 assert_eq!(cmd.return_type, "String");
644 assert!(!cmd.is_async);
645 assert!(cmd.serde_rename_all.is_none());
646 }
647
648 #[test]
649 fn test_new_for_test_parses_return_type_structure() {
650 let cmd = CommandInfo::new_for_test(
651 "get_users",
652 "src/api.rs",
653 20,
654 vec![],
655 "Vec<String>",
656 true,
657 vec![],
658 );
659
660 match cmd.return_type_structure {
662 TypeStructure::Array(inner) => match *inner {
663 TypeStructure::Primitive(name) => assert_eq!(name, "string"),
664 _ => panic!("Should be string primitive"),
665 },
666 _ => panic!("Should be Array"),
667 }
668 assert!(cmd.is_async);
669 }
670
671 #[test]
672 fn test_command_with_parameters() {
673 let param = ParameterInfo {
674 name: "user_id".to_string(),
675 rust_type: "String".to_string(),
676 is_optional: false,
677 type_structure: TypeStructure::Primitive("string".to_string()),
678 serde_rename: None,
679 };
680
681 let cmd = CommandInfo::new_for_test(
682 "get_user",
683 "src/api.rs",
684 30,
685 vec![param],
686 "User",
687 false,
688 vec![],
689 );
690
691 assert_eq!(cmd.parameters.len(), 1);
692 assert_eq!(cmd.parameters[0].name, "user_id");
693 assert_eq!(cmd.parameters[0].rust_type, "String");
694 }
695
696 #[test]
697 fn test_command_with_channels() {
698 let channel = ChannelInfo::new_for_test(
699 "progress",
700 "u32",
701 "download_file",
702 "src/download.rs",
703 40,
704 );
705
706 let cmd = CommandInfo::new_for_test(
707 "download_file",
708 "src/download.rs",
709 40,
710 vec![],
711 "Result<(), String>",
712 true,
713 vec![channel],
714 );
715
716 assert_eq!(cmd.channels.len(), 1);
717 assert_eq!(cmd.channels[0].parameter_name, "progress");
718 assert_eq!(cmd.channels[0].message_type, "u32");
719 }
720 }
721
722 mod channel_info {
724 use super::*;
725
726 #[test]
727 fn test_new_for_test_creates_valid_channel() {
728 let channel =
729 ChannelInfo::new_for_test("updates", "String", "subscribe", "src/events.rs", 50);
730
731 assert_eq!(channel.parameter_name, "updates");
732 assert_eq!(channel.message_type, "String");
733 assert_eq!(channel.command_name, "subscribe");
734 assert_eq!(channel.file_path, "src/events.rs");
735 assert_eq!(channel.line_number, 50);
736 assert!(channel.serde_rename.is_none());
737 }
738
739 #[test]
740 fn test_channel_parses_message_type_structure() {
741 let channel =
742 ChannelInfo::new_for_test("data", "Vec<u32>", "stream_data", "src/stream.rs", 60);
743
744 match channel.message_type_structure {
746 TypeStructure::Array(inner) => match *inner {
747 TypeStructure::Primitive(name) => assert_eq!(name, "number"),
748 _ => panic!("Should be number primitive"),
749 },
750 _ => panic!("Should be Array"),
751 }
752 }
753
754 #[test]
755 fn test_channel_clone() {
756 let original =
757 ChannelInfo::new_for_test("status", "bool", "monitor", "src/monitor.rs", 70);
758
759 let cloned = original.clone();
760 assert_eq!(cloned.parameter_name, "status");
761 assert_eq!(cloned.message_type, "bool");
762 assert_eq!(cloned.command_name, "monitor");
763 }
764 }
765
766 mod parameter_info {
768 use super::*;
769
770 #[test]
771 fn test_parameter_with_optional_type() {
772 let param = ParameterInfo {
773 name: "email".to_string(),
774 rust_type: "Option<String>".to_string(),
775 is_optional: true,
776 type_structure: TypeStructure::Optional(Box::new(TypeStructure::Primitive(
777 "string".to_string(),
778 ))),
779 serde_rename: None,
780 };
781
782 assert!(param.is_optional);
783 match param.type_structure {
784 TypeStructure::Optional(_) => (),
785 _ => panic!("Should be Optional"),
786 }
787 }
788
789 #[test]
790 fn test_parameter_with_serde_rename() {
791 let param = ParameterInfo {
792 name: "user_id".to_string(),
793 rust_type: "String".to_string(),
794 is_optional: false,
795 type_structure: TypeStructure::Primitive("string".to_string()),
796 serde_rename: Some("userId".to_string()),
797 };
798
799 assert_eq!(param.serde_rename, Some("userId".to_string()));
800 }
801 }
802
803 mod struct_info {
805 use super::*;
806
807 #[test]
808 fn test_struct_with_fields() {
809 let field = FieldInfo {
810 name: "name".to_string(),
811 rust_type: "String".to_string(),
812 is_optional: false,
813 is_public: true,
814 validator_attributes: None,
815 serde_rename: None,
816 type_structure: TypeStructure::Primitive("string".to_string()),
817 };
818
819 let struct_info = StructInfo {
820 name: "User".to_string(),
821 fields: vec![field],
822 file_path: "src/models.rs".to_string(),
823 is_enum: false,
824 serde_rename_all: None,
825 serde_tag: None,
826 enum_variants: None,
827 };
828
829 assert_eq!(struct_info.name, "User");
830 assert!(!struct_info.is_enum);
831 assert_eq!(struct_info.fields.len(), 1);
832 }
833
834 #[test]
835 fn test_enum_struct() {
836 let struct_info = StructInfo {
837 name: "Status".to_string(),
838 fields: vec![],
839 file_path: "src/types.rs".to_string(),
840 is_enum: true,
841 serde_rename_all: Some(RenameRule::CamelCase),
842 serde_tag: None,
843 enum_variants: None,
844 };
845
846 assert!(struct_info.is_enum);
847 assert!(struct_info.serde_rename_all.is_some());
848 }
849
850 #[test]
851 fn test_struct_clone() {
852 let original = StructInfo {
853 name: "Product".to_string(),
854 fields: vec![],
855 file_path: "src/product.rs".to_string(),
856 is_enum: false,
857 serde_rename_all: None,
858 serde_tag: None,
859 enum_variants: None,
860 };
861
862 let cloned = original.clone();
863 assert_eq!(cloned.name, "Product");
864 assert!(!cloned.is_enum);
865 }
866
867 #[test]
868 fn test_simple_enum_detection() {
869 let simple_enum = StructInfo {
871 name: "Status".to_string(),
872 fields: vec![
873 FieldInfo {
874 name: "Active".to_string(),
875 rust_type: "enum_variant".to_string(),
876 is_optional: false,
877 is_public: true,
878 validator_attributes: None,
879 serde_rename: None,
880 type_structure: TypeStructure::Custom("enum_variant".to_string()),
881 },
882 FieldInfo {
883 name: "Inactive".to_string(),
884 rust_type: "enum_variant".to_string(),
885 is_optional: false,
886 is_public: true,
887 validator_attributes: None,
888 serde_rename: None,
889 type_structure: TypeStructure::Custom("enum_variant".to_string()),
890 },
891 ],
892 file_path: "src/types.rs".to_string(),
893 is_enum: true,
894 serde_rename_all: None,
895 serde_tag: None,
896 enum_variants: None,
897 };
898
899 assert!(simple_enum.is_simple_enum());
900 assert!(!simple_enum.is_complex_enum());
901 }
902
903 #[test]
904 fn test_complex_enum_detection_via_fields() {
905 let complex_enum = StructInfo {
907 name: "Message".to_string(),
908 fields: vec![
909 FieldInfo {
910 name: "Quit".to_string(),
911 rust_type: "enum_variant".to_string(),
912 is_optional: false,
913 is_public: true,
914 validator_attributes: None,
915 serde_rename: None,
916 type_structure: TypeStructure::Custom("enum_variant".to_string()),
917 },
918 FieldInfo {
919 name: "Move".to_string(),
920 rust_type: "enum_variant_tuple".to_string(),
921 is_optional: false,
922 is_public: true,
923 validator_attributes: None,
924 serde_rename: None,
925 type_structure: TypeStructure::Custom("enum_variant".to_string()),
926 },
927 ],
928 file_path: "src/types.rs".to_string(),
929 is_enum: true,
930 serde_rename_all: None,
931 serde_tag: None,
932 enum_variants: None,
933 };
934
935 assert!(!complex_enum.is_simple_enum());
936 assert!(complex_enum.is_complex_enum());
937 }
938
939 #[test]
940 fn test_complex_enum_detection_via_enum_variants() {
941 let complex_enum = StructInfo {
943 name: "Message".to_string(),
944 fields: vec![],
945 file_path: "src/types.rs".to_string(),
946 is_enum: true,
947 serde_rename_all: None,
948 serde_tag: Some("type".to_string()),
949 enum_variants: Some(vec![
950 EnumVariantInfo {
951 name: "Quit".to_string(),
952 kind: EnumVariantKind::Unit,
953 serde_rename: None,
954 },
955 EnumVariantInfo {
956 name: "Move".to_string(),
957 kind: EnumVariantKind::Tuple(vec![
958 TypeStructure::Primitive("number".to_string()),
959 TypeStructure::Primitive("number".to_string()),
960 ]),
961 serde_rename: None,
962 },
963 ]),
964 };
965
966 assert!(!complex_enum.is_simple_enum());
967 assert!(complex_enum.is_complex_enum());
968 }
969
970 #[test]
971 fn test_discriminator_tag_default() {
972 let enum_info = StructInfo {
973 name: "Status".to_string(),
974 fields: vec![],
975 file_path: "src/types.rs".to_string(),
976 is_enum: true,
977 serde_rename_all: None,
978 serde_tag: None,
979 enum_variants: None,
980 };
981
982 assert_eq!(enum_info.discriminator_tag(), "type");
983 }
984
985 #[test]
986 fn test_discriminator_tag_custom() {
987 let enum_info = StructInfo {
988 name: "Status".to_string(),
989 fields: vec![],
990 file_path: "src/types.rs".to_string(),
991 is_enum: true,
992 serde_rename_all: None,
993 serde_tag: Some("kind".to_string()),
994 enum_variants: None,
995 };
996
997 assert_eq!(enum_info.discriminator_tag(), "kind");
998 }
999 }
1000
1001 mod enum_variant_kind {
1003 use super::*;
1004
1005 #[test]
1006 fn test_unit_variant() {
1007 let kind = EnumVariantKind::Unit;
1008 assert_eq!(kind, EnumVariantKind::Unit);
1009 }
1010
1011 #[test]
1012 fn test_tuple_variant_single_field() {
1013 let kind = EnumVariantKind::Tuple(vec![TypeStructure::Primitive("string".to_string())]);
1014
1015 match kind {
1016 EnumVariantKind::Tuple(fields) => {
1017 assert_eq!(fields.len(), 1);
1018 assert_eq!(fields[0], TypeStructure::Primitive("string".to_string()));
1019 }
1020 _ => panic!("Should be Tuple variant"),
1021 }
1022 }
1023
1024 #[test]
1025 fn test_tuple_variant_multiple_fields() {
1026 let kind = EnumVariantKind::Tuple(vec![
1027 TypeStructure::Primitive("number".to_string()),
1028 TypeStructure::Primitive("number".to_string()),
1029 ]);
1030
1031 match kind {
1032 EnumVariantKind::Tuple(fields) => {
1033 assert_eq!(fields.len(), 2);
1034 }
1035 _ => panic!("Should be Tuple variant"),
1036 }
1037 }
1038
1039 #[test]
1040 fn test_struct_variant() {
1041 let fields = vec![
1042 FieldInfo {
1043 name: "r".to_string(),
1044 rust_type: "u8".to_string(),
1045 is_optional: false,
1046 is_public: true,
1047 validator_attributes: None,
1048 serde_rename: None,
1049 type_structure: TypeStructure::Primitive("number".to_string()),
1050 },
1051 FieldInfo {
1052 name: "g".to_string(),
1053 rust_type: "u8".to_string(),
1054 is_optional: false,
1055 is_public: true,
1056 validator_attributes: None,
1057 serde_rename: None,
1058 type_structure: TypeStructure::Primitive("number".to_string()),
1059 },
1060 ];
1061 let kind = EnumVariantKind::Struct(fields);
1062
1063 match kind {
1064 EnumVariantKind::Struct(f) => {
1065 assert_eq!(f.len(), 2);
1066 assert_eq!(f[0].name, "r");
1067 assert_eq!(f[1].name, "g");
1068 }
1069 _ => panic!("Should be Struct variant"),
1070 }
1071 }
1072
1073 #[test]
1074 fn test_serialize_deserialize() {
1075 let unit = EnumVariantKind::Unit;
1076 let json = serde_json::to_string(&unit).unwrap();
1077 let deserialized: EnumVariantKind = serde_json::from_str(&json).unwrap();
1078 assert_eq!(deserialized, EnumVariantKind::Unit);
1079
1080 let tuple =
1081 EnumVariantKind::Tuple(vec![TypeStructure::Primitive("string".to_string())]);
1082 let json = serde_json::to_string(&tuple).unwrap();
1083 let deserialized: EnumVariantKind = serde_json::from_str(&json).unwrap();
1084 match deserialized {
1085 EnumVariantKind::Tuple(fields) => assert_eq!(fields.len(), 1),
1086 _ => panic!("Should deserialize to Tuple"),
1087 }
1088 }
1089 }
1090
1091 mod enum_variant_info {
1093 use super::*;
1094
1095 #[test]
1096 fn test_unit_variant_helpers() {
1097 let variant = EnumVariantInfo {
1098 name: "Quit".to_string(),
1099 kind: EnumVariantKind::Unit,
1100 serde_rename: None,
1101 };
1102
1103 assert!(variant.is_unit());
1104 assert!(!variant.is_tuple());
1105 assert!(!variant.is_struct());
1106 assert!(variant.tuple_fields().is_none());
1107 assert!(variant.struct_fields().is_none());
1108 }
1109
1110 #[test]
1111 fn test_tuple_variant_helpers() {
1112 let variant = EnumVariantInfo {
1113 name: "Move".to_string(),
1114 kind: EnumVariantKind::Tuple(vec![
1115 TypeStructure::Primitive("number".to_string()),
1116 TypeStructure::Primitive("number".to_string()),
1117 ]),
1118 serde_rename: None,
1119 };
1120
1121 assert!(!variant.is_unit());
1122 assert!(variant.is_tuple());
1123 assert!(!variant.is_struct());
1124
1125 let fields = variant.tuple_fields().unwrap();
1126 assert_eq!(fields.len(), 2);
1127 assert!(variant.struct_fields().is_none());
1128 }
1129
1130 #[test]
1131 fn test_struct_variant_helpers() {
1132 let variant = EnumVariantInfo {
1133 name: "ChangeColor".to_string(),
1134 kind: EnumVariantKind::Struct(vec![FieldInfo {
1135 name: "r".to_string(),
1136 rust_type: "u8".to_string(),
1137 is_optional: false,
1138 is_public: true,
1139 validator_attributes: None,
1140 serde_rename: None,
1141 type_structure: TypeStructure::Primitive("number".to_string()),
1142 }]),
1143 serde_rename: None,
1144 };
1145
1146 assert!(!variant.is_unit());
1147 assert!(!variant.is_tuple());
1148 assert!(variant.is_struct());
1149
1150 assert!(variant.tuple_fields().is_none());
1151 let fields = variant.struct_fields().unwrap();
1152 assert_eq!(fields.len(), 1);
1153 assert_eq!(fields[0].name, "r");
1154 }
1155
1156 #[test]
1157 fn test_variant_with_serde_rename() {
1158 let variant = EnumVariantInfo {
1159 name: "Quit".to_string(),
1160 kind: EnumVariantKind::Unit,
1161 serde_rename: Some("quit".to_string()),
1162 };
1163
1164 assert_eq!(variant.serde_rename, Some("quit".to_string()));
1165 }
1166
1167 #[test]
1168 fn test_clone() {
1169 let original = EnumVariantInfo {
1170 name: "Write".to_string(),
1171 kind: EnumVariantKind::Tuple(vec![TypeStructure::Primitive("string".to_string())]),
1172 serde_rename: None,
1173 };
1174
1175 let cloned = original.clone();
1176 assert_eq!(cloned.name, "Write");
1177 assert!(cloned.is_tuple());
1178 }
1179 }
1180
1181 mod field_info {
1183 use super::*;
1184
1185 #[test]
1186 fn test_field_with_validator() {
1187 let validator = ValidatorAttributes {
1188 length: Some(LengthConstraint {
1189 min: Some(1),
1190 max: Some(100),
1191 message: None,
1192 }),
1193 range: None,
1194 email: false,
1195 url: false,
1196 custom_message: None,
1197 };
1198
1199 let field = FieldInfo {
1200 name: "username".to_string(),
1201 rust_type: "String".to_string(),
1202 is_optional: false,
1203 is_public: true,
1204 validator_attributes: Some(validator),
1205 serde_rename: None,
1206 type_structure: TypeStructure::Primitive("string".to_string()),
1207 };
1208
1209 assert!(field.validator_attributes.is_some());
1210 let attrs = field.validator_attributes.unwrap();
1211 assert!(attrs.length.is_some());
1212 }
1213
1214 #[test]
1215 fn test_private_field() {
1216 let field = FieldInfo {
1217 name: "internal_id".to_string(),
1218 rust_type: "u64".to_string(),
1219 is_optional: false,
1220 is_public: false,
1221 validator_attributes: None,
1222 serde_rename: None,
1223 type_structure: TypeStructure::Primitive("number".to_string()),
1224 };
1225
1226 assert!(!field.is_public);
1227 }
1228
1229 #[test]
1230 fn test_field_with_serde_rename() {
1231 let field = FieldInfo {
1232 name: "created_at".to_string(),
1233 rust_type: "String".to_string(),
1234 is_optional: true,
1235 is_public: true,
1236 validator_attributes: None,
1237 serde_rename: Some("createdAt".to_string()),
1238 type_structure: TypeStructure::Optional(Box::new(TypeStructure::Primitive(
1239 "string".to_string(),
1240 ))),
1241 };
1242
1243 assert_eq!(field.serde_rename, Some("createdAt".to_string()));
1244 assert!(field.is_optional);
1245 }
1246
1247 #[test]
1248 fn test_field_clone() {
1249 let original = FieldInfo {
1250 name: "count".to_string(),
1251 rust_type: "i32".to_string(),
1252 is_optional: false,
1253 is_public: true,
1254 validator_attributes: None,
1255 serde_rename: None,
1256 type_structure: TypeStructure::Primitive("number".to_string()),
1257 };
1258
1259 let cloned = original.clone();
1260 assert_eq!(cloned.name, "count");
1261 assert_eq!(cloned.rust_type, "i32");
1262 }
1263 }
1264
1265 mod event_info {
1267 use super::*;
1268
1269 #[test]
1270 fn test_event_info_creation() {
1271 let event = EventInfo {
1272 event_name: "user-updated".to_string(),
1273 payload_type: "User".to_string(),
1274 payload_type_structure: TypeStructure::Custom("User".to_string()),
1275 file_path: "src/events.rs".to_string(),
1276 line_number: 100,
1277 };
1278
1279 assert_eq!(event.event_name, "user-updated");
1280 assert_eq!(event.payload_type, "User");
1281 match event.payload_type_structure {
1282 TypeStructure::Custom(name) => assert_eq!(name, "User"),
1283 _ => panic!("Should be Custom type"),
1284 }
1285 }
1286
1287 #[test]
1288 fn test_event_with_primitive_payload() {
1289 let event = EventInfo {
1290 event_name: "progress".to_string(),
1291 payload_type: "u32".to_string(),
1292 payload_type_structure: TypeStructure::Primitive("number".to_string()),
1293 file_path: "src/progress.rs".to_string(),
1294 line_number: 50,
1295 };
1296
1297 match event.payload_type_structure {
1298 TypeStructure::Primitive(name) => assert_eq!(name, "number"),
1299 _ => panic!("Should be Primitive type"),
1300 }
1301 }
1302 }
1303}