tauri_typegen/
models.rs

1use serde::{Deserialize, Serialize};
2use serde_rename_rule::RenameRule;
3
4/// Represents the structure of a type for code generation
5/// This allows generators to work with parsed type information instead of string parsing
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub enum TypeStructure {
9    /// Primitive types: "string", "number", "boolean", "void"
10    Primitive(String),
11
12    /// Array/Vec types: `Vec<T>` -> `Array(T)`
13    Array(Box<TypeStructure>),
14
15    /// Map types: `HashMap<K, V>`, `BTreeMap<K, V>` -> `Map { key: K, value: V }`
16    Map {
17        key: Box<TypeStructure>,
18        value: Box<TypeStructure>,
19    },
20
21    /// Set types: `HashSet<T>`, `BTreeSet<T>` -> `Set(T)`
22    Set(Box<TypeStructure>),
23
24    /// Tuple types: `(T, U, V)` -> `Tuple([T, U, V])`
25    Tuple(Vec<TypeStructure>),
26
27    /// Optional types: `Option<T>` -> `Optional(T)`
28    Optional(Box<TypeStructure>),
29
30    /// Result types: `Result<T, E>` -> `Result(T)` (error type ignored for TS)
31    Result(Box<TypeStructure>),
32
33    /// Custom/User-defined types
34    Custom(String),
35}
36
37impl Default for TypeStructure {
38    fn default() -> Self {
39        // Default to string for test compatibility
40        TypeStructure::Primitive("string".to_string())
41    }
42}
43
44pub struct CommandInfo {
45    pub name: String,
46    pub file_path: String,
47    pub line_number: usize,
48    pub parameters: Vec<ParameterInfo>,
49    pub return_type: String, // Rust return type (e.g., "Vec<Banana>")
50    /// Structured representation of the return type for generators
51    pub return_type_structure: TypeStructure,
52    pub is_async: bool,
53    pub channels: Vec<ChannelInfo>,
54    /// Serde rename_all attribute: #[serde(rename_all = "...")]
55    /// Applied to command function, affects parameter/channel serialization
56    pub serde_rename_all: Option<RenameRule>,
57}
58
59impl CommandInfo {
60    /// Helper for tests: Create a CommandInfo
61    #[doc(hidden)]
62    pub fn new_for_test(
63        name: impl Into<String>,
64        file_path: impl Into<String>,
65        line_number: usize,
66        parameters: Vec<ParameterInfo>,
67        return_type: impl Into<String>,
68        is_async: bool,
69        channels: Vec<ChannelInfo>,
70    ) -> Self {
71        use crate::analysis::type_resolver::TypeResolver;
72        let return_type_str = return_type.into();
73        let type_resolver = TypeResolver::new();
74        let return_type_structure = type_resolver.parse_type_structure(&return_type_str);
75
76        Self {
77            name: name.into(),
78            file_path: file_path.into(),
79            line_number,
80            parameters,
81            return_type: return_type_str,
82            return_type_structure,
83            is_async,
84            channels,
85            serde_rename_all: None,
86        }
87    }
88}
89
90pub struct ParameterInfo {
91    pub name: String,
92    pub rust_type: String,
93    pub is_optional: bool,
94    /// Structured representation of the type for generators
95    pub type_structure: TypeStructure,
96    /// Serde rename attribute (optional, for future extensibility)
97    /// Parameters are serialized following Tauri/JS conventions (camelCase)
98    pub serde_rename: Option<String>,
99}
100
101#[derive(Clone, Debug)]
102pub struct StructInfo {
103    pub name: String,
104    pub fields: Vec<FieldInfo>,
105    pub file_path: String,
106    pub is_enum: bool,
107    /// Serde rename_all attribute: #[serde(rename_all = "...")]
108    pub serde_rename_all: Option<RenameRule>,
109}
110
111#[derive(Clone, Debug)]
112pub struct FieldInfo {
113    pub name: String,
114    pub rust_type: String,
115    pub is_optional: bool,
116    pub is_public: bool,
117    pub validator_attributes: Option<ValidatorAttributes>,
118    /// Serde rename attribute: #[serde(rename = "...")]
119    pub serde_rename: Option<String>,
120    /// Structured representation of the type for generators
121    pub type_structure: TypeStructure,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct ValidatorAttributes {
127    pub length: Option<LengthConstraint>,
128    pub range: Option<RangeConstraint>,
129    pub email: bool,
130    pub url: bool,
131    pub custom_message: Option<String>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct LengthConstraint {
137    pub min: Option<u64>,
138    pub max: Option<u64>,
139    pub message: Option<String>,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(rename_all = "camelCase")]
144pub struct RangeConstraint {
145    pub min: Option<f64>,
146    pub max: Option<f64>,
147    pub message: Option<String>,
148}
149
150// Event information for frontend event listeners
151pub struct EventInfo {
152    pub event_name: String,
153    pub payload_type: String,
154    /// Structured representation of the payload type for generators
155    pub payload_type_structure: TypeStructure,
156    pub file_path: String,
157    pub line_number: usize,
158}
159
160// Channel information for streaming data from Rust to frontend
161#[derive(Clone)]
162pub struct ChannelInfo {
163    pub parameter_name: String,
164    pub message_type: String,
165    pub command_name: String,
166    pub file_path: String,
167    pub line_number: usize,
168    /// Serde rename attribute (optional, for future extensibility)
169    /// Channel parameters are serialized following Tauri/JS conventions (camelCase)
170    pub serde_rename: Option<String>,
171    /// Structured representation of the message type for generators
172    pub message_type_structure: TypeStructure,
173}
174
175impl ChannelInfo {
176    /// Helper for tests: Create a ChannelInfo
177    #[doc(hidden)]
178    pub fn new_for_test(
179        parameter_name: impl Into<String>,
180        message_type: impl Into<String>,
181        command_name: impl Into<String>,
182        file_path: impl Into<String>,
183        line_number: usize,
184    ) -> Self {
185        let message_type_str = message_type.into();
186        Self {
187            parameter_name: parameter_name.into(),
188            message_type: message_type_str.clone(),
189            command_name: command_name.into(),
190            file_path: file_path.into(),
191            line_number,
192            serde_rename: None,
193            // Parse message_type into TypeStructure for tests
194            message_type_structure: crate::analysis::type_resolver::TypeResolver::new()
195                .parse_type_structure(&message_type_str),
196        }
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    // TypeStructure tests
205    mod type_structure {
206        use super::*;
207
208        #[test]
209        fn test_default_is_string_primitive() {
210            let default_type = TypeStructure::default();
211            match default_type {
212                TypeStructure::Primitive(name) => assert_eq!(name, "string"),
213                _ => panic!("Default should be Primitive(\"string\")"),
214            }
215        }
216
217        #[test]
218        fn test_primitive_variants() {
219            let types = vec!["string", "number", "boolean", "void"];
220            for type_name in types {
221                let primitive = TypeStructure::Primitive(type_name.to_string());
222                match primitive {
223                    TypeStructure::Primitive(name) => assert_eq!(name, type_name),
224                    _ => panic!("Should be Primitive variant"),
225                }
226            }
227        }
228
229        #[test]
230        fn test_array_wraps_inner_type() {
231            let inner = TypeStructure::Primitive("number".to_string());
232            let array = TypeStructure::Array(Box::new(inner));
233
234            match array {
235                TypeStructure::Array(boxed) => match *boxed {
236                    TypeStructure::Primitive(name) => assert_eq!(name, "number"),
237                    _ => panic!("Inner should be Primitive"),
238                },
239                _ => panic!("Should be Array variant"),
240            }
241        }
242
243        #[test]
244        fn test_map_has_key_and_value() {
245            let key = TypeStructure::Primitive("string".to_string());
246            let value = TypeStructure::Primitive("number".to_string());
247            let map = TypeStructure::Map {
248                key: Box::new(key),
249                value: Box::new(value),
250            };
251
252            match map {
253                TypeStructure::Map { key, value } => match (*key, *value) {
254                    (TypeStructure::Primitive(k), TypeStructure::Primitive(v)) => {
255                        assert_eq!(k, "string");
256                        assert_eq!(v, "number");
257                    }
258                    _ => panic!("Key and value should be Primitives"),
259                },
260                _ => panic!("Should be Map variant"),
261            }
262        }
263
264        #[test]
265        fn test_set_wraps_inner_type() {
266            let inner = TypeStructure::Primitive("string".to_string());
267            let set = TypeStructure::Set(Box::new(inner));
268
269            match set {
270                TypeStructure::Set(boxed) => match *boxed {
271                    TypeStructure::Primitive(name) => assert_eq!(name, "string"),
272                    _ => panic!("Inner should be Primitive"),
273                },
274                _ => panic!("Should be Set variant"),
275            }
276        }
277
278        #[test]
279        fn test_tuple_with_multiple_types() {
280            let types = vec![
281                TypeStructure::Primitive("string".to_string()),
282                TypeStructure::Primitive("number".to_string()),
283                TypeStructure::Primitive("boolean".to_string()),
284            ];
285            let tuple = TypeStructure::Tuple(types);
286
287            match tuple {
288                TypeStructure::Tuple(types) => {
289                    assert_eq!(types.len(), 3);
290                    match &types[0] {
291                        TypeStructure::Primitive(name) => assert_eq!(name, "string"),
292                        _ => panic!("First type should be string"),
293                    }
294                }
295                _ => panic!("Should be Tuple variant"),
296            }
297        }
298
299        #[test]
300        fn test_empty_tuple() {
301            let tuple = TypeStructure::Tuple(vec![]);
302            match tuple {
303                TypeStructure::Tuple(types) => assert_eq!(types.len(), 0),
304                _ => panic!("Should be Tuple variant"),
305            }
306        }
307
308        #[test]
309        fn test_optional_wraps_inner_type() {
310            let inner = TypeStructure::Custom("User".to_string());
311            let optional = TypeStructure::Optional(Box::new(inner));
312
313            match optional {
314                TypeStructure::Optional(boxed) => match *boxed {
315                    TypeStructure::Custom(name) => assert_eq!(name, "User"),
316                    _ => panic!("Inner should be Custom"),
317                },
318                _ => panic!("Should be Optional variant"),
319            }
320        }
321
322        #[test]
323        fn test_result_wraps_success_type() {
324            let success = TypeStructure::Primitive("string".to_string());
325            let result = TypeStructure::Result(Box::new(success));
326
327            match result {
328                TypeStructure::Result(boxed) => match *boxed {
329                    TypeStructure::Primitive(name) => assert_eq!(name, "string"),
330                    _ => panic!("Inner should be Primitive"),
331                },
332                _ => panic!("Should be Result variant"),
333            }
334        }
335
336        #[test]
337        fn test_custom_type() {
338            let custom = TypeStructure::Custom("UserProfile".to_string());
339            match custom {
340                TypeStructure::Custom(name) => assert_eq!(name, "UserProfile"),
341                _ => panic!("Should be Custom variant"),
342            }
343        }
344
345        #[test]
346        fn test_nested_structures() {
347            // Vec<Option<HashMap<String, User>>>
348            let user = TypeStructure::Custom("User".to_string());
349            let map = TypeStructure::Map {
350                key: Box::new(TypeStructure::Primitive("string".to_string())),
351                value: Box::new(user),
352            };
353            let optional = TypeStructure::Optional(Box::new(map));
354            let array = TypeStructure::Array(Box::new(optional));
355
356            match array {
357                TypeStructure::Array(arr_inner) => match *arr_inner {
358                    TypeStructure::Optional(opt_inner) => match *opt_inner {
359                        TypeStructure::Map { key, value } => match (*key, *value) {
360                            (TypeStructure::Primitive(k), TypeStructure::Custom(v)) => {
361                                assert_eq!(k, "string");
362                                assert_eq!(v, "User");
363                            }
364                            _ => panic!("Map types incorrect"),
365                        },
366                        _ => panic!("Should be Map"),
367                    },
368                    _ => panic!("Should be Optional"),
369                },
370                _ => panic!("Should be Array"),
371            }
372        }
373
374        #[test]
375        fn test_clone_type_structure() {
376            let original = TypeStructure::Primitive("string".to_string());
377            let cloned = original.clone();
378
379            match (original, cloned) {
380                (TypeStructure::Primitive(o), TypeStructure::Primitive(c)) => {
381                    assert_eq!(o, c);
382                }
383                _ => panic!("Clone should maintain variant"),
384            }
385        }
386
387        #[test]
388        fn test_serialize_deserialize_primitive() {
389            let primitive = TypeStructure::Primitive("number".to_string());
390            let json = serde_json::to_string(&primitive).unwrap();
391            let deserialized: TypeStructure = serde_json::from_str(&json).unwrap();
392
393            match deserialized {
394                TypeStructure::Primitive(name) => assert_eq!(name, "number"),
395                _ => panic!("Should deserialize to Primitive"),
396            }
397        }
398
399        #[test]
400        fn test_serialize_deserialize_complex() {
401            let complex = TypeStructure::Array(Box::new(TypeStructure::Optional(Box::new(
402                TypeStructure::Custom("User".to_string()),
403            ))));
404
405            let json = serde_json::to_string(&complex).unwrap();
406            let deserialized: TypeStructure = serde_json::from_str(&json).unwrap();
407
408            match deserialized {
409                TypeStructure::Array(arr) => match *arr {
410                    TypeStructure::Optional(opt) => match *opt {
411                        TypeStructure::Custom(name) => assert_eq!(name, "User"),
412                        _ => panic!("Should be Custom"),
413                    },
414                    _ => panic!("Should be Optional"),
415                },
416                _ => panic!("Should be Array"),
417            }
418        }
419    }
420
421    // ValidatorAttributes tests
422    mod validator_attributes {
423        use super::*;
424
425        #[test]
426        fn test_length_constraint() {
427            let length = LengthConstraint {
428                min: Some(5),
429                max: Some(100),
430                message: Some("Invalid length".to_string()),
431            };
432
433            assert_eq!(length.min, Some(5));
434            assert_eq!(length.max, Some(100));
435            assert_eq!(length.message, Some("Invalid length".to_string()));
436        }
437
438        #[test]
439        fn test_range_constraint() {
440            let range = RangeConstraint {
441                min: Some(0.0),
442                max: Some(10.5),
443                message: Some("Out of range".to_string()),
444            };
445
446            assert_eq!(range.min, Some(0.0));
447            assert_eq!(range.max, Some(10.5));
448            assert_eq!(range.message, Some("Out of range".to_string()));
449        }
450
451        #[test]
452        fn test_validator_attributes_email() {
453            let validator = ValidatorAttributes {
454                length: None,
455                range: None,
456                email: true,
457                url: false,
458                custom_message: None,
459            };
460
461            assert!(validator.email);
462            assert!(!validator.url);
463        }
464
465        #[test]
466        fn test_validator_attributes_with_length() {
467            let validator = ValidatorAttributes {
468                length: Some(LengthConstraint {
469                    min: Some(1),
470                    max: Some(50),
471                    message: None,
472                }),
473                range: None,
474                email: false,
475                url: false,
476                custom_message: None,
477            };
478
479            assert!(validator.length.is_some());
480            let length = validator.length.unwrap();
481            assert_eq!(length.min, Some(1));
482            assert_eq!(length.max, Some(50));
483        }
484
485        #[test]
486        fn test_serialize_validator_attributes() {
487            let validator = ValidatorAttributes {
488                length: Some(LengthConstraint {
489                    min: Some(5),
490                    max: Some(100),
491                    message: None,
492                }),
493                range: None,
494                email: true,
495                url: false,
496                custom_message: Some("Custom error".to_string()),
497            };
498
499            let json = serde_json::to_string(&validator).unwrap();
500            let deserialized: ValidatorAttributes = serde_json::from_str(&json).unwrap();
501
502            assert!(deserialized.email);
503            assert_eq!(
504                deserialized.custom_message,
505                Some("Custom error".to_string())
506            );
507            assert!(deserialized.length.is_some());
508        }
509
510        #[test]
511        fn test_validator_attributes_clone() {
512            let original = ValidatorAttributes {
513                length: None,
514                range: Some(RangeConstraint {
515                    min: Some(0.0),
516                    max: Some(1.0),
517                    message: None,
518                }),
519                email: false,
520                url: true,
521                custom_message: None,
522            };
523
524            let cloned = original.clone();
525            assert!(cloned.url);
526            assert!(cloned.range.is_some());
527        }
528    }
529
530    // CommandInfo tests
531    mod command_info {
532        use super::*;
533
534        #[test]
535        fn test_new_for_test_creates_valid_command() {
536            let params = vec![];
537            let channels = vec![];
538
539            let cmd = CommandInfo::new_for_test(
540                "greet",
541                "src/main.rs",
542                10,
543                params,
544                "String",
545                false,
546                channels,
547            );
548
549            assert_eq!(cmd.name, "greet");
550            assert_eq!(cmd.file_path, "src/main.rs");
551            assert_eq!(cmd.line_number, 10);
552            assert_eq!(cmd.return_type, "String");
553            assert!(!cmd.is_async);
554            assert!(cmd.serde_rename_all.is_none());
555        }
556
557        #[test]
558        fn test_new_for_test_parses_return_type_structure() {
559            let cmd = CommandInfo::new_for_test(
560                "get_users",
561                "src/api.rs",
562                20,
563                vec![],
564                "Vec<String>",
565                true,
566                vec![],
567            );
568
569            // Should parse Vec<String> into Array(Primitive("string"))
570            match cmd.return_type_structure {
571                TypeStructure::Array(inner) => match *inner {
572                    TypeStructure::Primitive(name) => assert_eq!(name, "string"),
573                    _ => panic!("Should be string primitive"),
574                },
575                _ => panic!("Should be Array"),
576            }
577            assert!(cmd.is_async);
578        }
579
580        #[test]
581        fn test_command_with_parameters() {
582            let param = ParameterInfo {
583                name: "user_id".to_string(),
584                rust_type: "String".to_string(),
585                is_optional: false,
586                type_structure: TypeStructure::Primitive("string".to_string()),
587                serde_rename: None,
588            };
589
590            let cmd = CommandInfo::new_for_test(
591                "get_user",
592                "src/api.rs",
593                30,
594                vec![param],
595                "User",
596                false,
597                vec![],
598            );
599
600            assert_eq!(cmd.parameters.len(), 1);
601            assert_eq!(cmd.parameters[0].name, "user_id");
602            assert_eq!(cmd.parameters[0].rust_type, "String");
603        }
604
605        #[test]
606        fn test_command_with_channels() {
607            let channel = ChannelInfo::new_for_test(
608                "progress",
609                "u32",
610                "download_file",
611                "src/download.rs",
612                40,
613            );
614
615            let cmd = CommandInfo::new_for_test(
616                "download_file",
617                "src/download.rs",
618                40,
619                vec![],
620                "Result<(), String>",
621                true,
622                vec![channel],
623            );
624
625            assert_eq!(cmd.channels.len(), 1);
626            assert_eq!(cmd.channels[0].parameter_name, "progress");
627            assert_eq!(cmd.channels[0].message_type, "u32");
628        }
629    }
630
631    // ChannelInfo tests
632    mod channel_info {
633        use super::*;
634
635        #[test]
636        fn test_new_for_test_creates_valid_channel() {
637            let channel =
638                ChannelInfo::new_for_test("updates", "String", "subscribe", "src/events.rs", 50);
639
640            assert_eq!(channel.parameter_name, "updates");
641            assert_eq!(channel.message_type, "String");
642            assert_eq!(channel.command_name, "subscribe");
643            assert_eq!(channel.file_path, "src/events.rs");
644            assert_eq!(channel.line_number, 50);
645            assert!(channel.serde_rename.is_none());
646        }
647
648        #[test]
649        fn test_channel_parses_message_type_structure() {
650            let channel =
651                ChannelInfo::new_for_test("data", "Vec<u32>", "stream_data", "src/stream.rs", 60);
652
653            // Should parse Vec<u32> into Array(Primitive("number"))
654            match channel.message_type_structure {
655                TypeStructure::Array(inner) => match *inner {
656                    TypeStructure::Primitive(name) => assert_eq!(name, "number"),
657                    _ => panic!("Should be number primitive"),
658                },
659                _ => panic!("Should be Array"),
660            }
661        }
662
663        #[test]
664        fn test_channel_clone() {
665            let original =
666                ChannelInfo::new_for_test("status", "bool", "monitor", "src/monitor.rs", 70);
667
668            let cloned = original.clone();
669            assert_eq!(cloned.parameter_name, "status");
670            assert_eq!(cloned.message_type, "bool");
671            assert_eq!(cloned.command_name, "monitor");
672        }
673    }
674
675    // ParameterInfo tests
676    mod parameter_info {
677        use super::*;
678
679        #[test]
680        fn test_parameter_with_optional_type() {
681            let param = ParameterInfo {
682                name: "email".to_string(),
683                rust_type: "Option<String>".to_string(),
684                is_optional: true,
685                type_structure: TypeStructure::Optional(Box::new(TypeStructure::Primitive(
686                    "string".to_string(),
687                ))),
688                serde_rename: None,
689            };
690
691            assert!(param.is_optional);
692            match param.type_structure {
693                TypeStructure::Optional(_) => (),
694                _ => panic!("Should be Optional"),
695            }
696        }
697
698        #[test]
699        fn test_parameter_with_serde_rename() {
700            let param = ParameterInfo {
701                name: "user_id".to_string(),
702                rust_type: "String".to_string(),
703                is_optional: false,
704                type_structure: TypeStructure::Primitive("string".to_string()),
705                serde_rename: Some("userId".to_string()),
706            };
707
708            assert_eq!(param.serde_rename, Some("userId".to_string()));
709        }
710    }
711
712    // StructInfo tests
713    mod struct_info {
714        use super::*;
715
716        #[test]
717        fn test_struct_with_fields() {
718            let field = FieldInfo {
719                name: "name".to_string(),
720                rust_type: "String".to_string(),
721                is_optional: false,
722                is_public: true,
723                validator_attributes: None,
724                serde_rename: None,
725                type_structure: TypeStructure::Primitive("string".to_string()),
726            };
727
728            let struct_info = StructInfo {
729                name: "User".to_string(),
730                fields: vec![field],
731                file_path: "src/models.rs".to_string(),
732                is_enum: false,
733                serde_rename_all: None,
734            };
735
736            assert_eq!(struct_info.name, "User");
737            assert!(!struct_info.is_enum);
738            assert_eq!(struct_info.fields.len(), 1);
739        }
740
741        #[test]
742        fn test_enum_struct() {
743            let struct_info = StructInfo {
744                name: "Status".to_string(),
745                fields: vec![],
746                file_path: "src/types.rs".to_string(),
747                is_enum: true,
748                serde_rename_all: Some(RenameRule::CamelCase),
749            };
750
751            assert!(struct_info.is_enum);
752            assert!(struct_info.serde_rename_all.is_some());
753        }
754
755        #[test]
756        fn test_struct_clone() {
757            let original = StructInfo {
758                name: "Product".to_string(),
759                fields: vec![],
760                file_path: "src/product.rs".to_string(),
761                is_enum: false,
762                serde_rename_all: None,
763            };
764
765            let cloned = original.clone();
766            assert_eq!(cloned.name, "Product");
767            assert!(!cloned.is_enum);
768        }
769    }
770
771    // FieldInfo tests
772    mod field_info {
773        use super::*;
774
775        #[test]
776        fn test_field_with_validator() {
777            let validator = ValidatorAttributes {
778                length: Some(LengthConstraint {
779                    min: Some(1),
780                    max: Some(100),
781                    message: None,
782                }),
783                range: None,
784                email: false,
785                url: false,
786                custom_message: None,
787            };
788
789            let field = FieldInfo {
790                name: "username".to_string(),
791                rust_type: "String".to_string(),
792                is_optional: false,
793                is_public: true,
794                validator_attributes: Some(validator),
795                serde_rename: None,
796                type_structure: TypeStructure::Primitive("string".to_string()),
797            };
798
799            assert!(field.validator_attributes.is_some());
800            let attrs = field.validator_attributes.unwrap();
801            assert!(attrs.length.is_some());
802        }
803
804        #[test]
805        fn test_private_field() {
806            let field = FieldInfo {
807                name: "internal_id".to_string(),
808                rust_type: "u64".to_string(),
809                is_optional: false,
810                is_public: false,
811                validator_attributes: None,
812                serde_rename: None,
813                type_structure: TypeStructure::Primitive("number".to_string()),
814            };
815
816            assert!(!field.is_public);
817        }
818
819        #[test]
820        fn test_field_with_serde_rename() {
821            let field = FieldInfo {
822                name: "created_at".to_string(),
823                rust_type: "String".to_string(),
824                is_optional: true,
825                is_public: true,
826                validator_attributes: None,
827                serde_rename: Some("createdAt".to_string()),
828                type_structure: TypeStructure::Optional(Box::new(TypeStructure::Primitive(
829                    "string".to_string(),
830                ))),
831            };
832
833            assert_eq!(field.serde_rename, Some("createdAt".to_string()));
834            assert!(field.is_optional);
835        }
836
837        #[test]
838        fn test_field_clone() {
839            let original = FieldInfo {
840                name: "count".to_string(),
841                rust_type: "i32".to_string(),
842                is_optional: false,
843                is_public: true,
844                validator_attributes: None,
845                serde_rename: None,
846                type_structure: TypeStructure::Primitive("number".to_string()),
847            };
848
849            let cloned = original.clone();
850            assert_eq!(cloned.name, "count");
851            assert_eq!(cloned.rust_type, "i32");
852        }
853    }
854
855    // EventInfo tests
856    mod event_info {
857        use super::*;
858
859        #[test]
860        fn test_event_info_creation() {
861            let event = EventInfo {
862                event_name: "user-updated".to_string(),
863                payload_type: "User".to_string(),
864                payload_type_structure: TypeStructure::Custom("User".to_string()),
865                file_path: "src/events.rs".to_string(),
866                line_number: 100,
867            };
868
869            assert_eq!(event.event_name, "user-updated");
870            assert_eq!(event.payload_type, "User");
871            match event.payload_type_structure {
872                TypeStructure::Custom(name) => assert_eq!(name, "User"),
873                _ => panic!("Should be Custom type"),
874            }
875        }
876
877        #[test]
878        fn test_event_with_primitive_payload() {
879            let event = EventInfo {
880                event_name: "progress".to_string(),
881                payload_type: "u32".to_string(),
882                payload_type_structure: TypeStructure::Primitive("number".to_string()),
883                file_path: "src/progress.rs".to_string(),
884                line_number: 50,
885            };
886
887            match event.payload_type_structure {
888                TypeStructure::Primitive(name) => assert_eq!(name, "number"),
889                _ => panic!("Should be Primitive type"),
890            }
891        }
892    }
893}