@@ -9,7 +9,7 @@ use quote::quote;
99use super :: {
1010 circular:: detect_circular_fields,
1111 file_lookup:: find_model_from_schema_path,
12- seaorm:: RelationFieldInfo ,
12+ seaorm:: { RelationFieldInfo , convert_type_with_chrono } ,
1313 type_utils:: { capitalize_first, is_seaorm_relation_type} ,
1414} ;
1515use crate :: parser:: { extract_rename_all, extract_skip} ;
@@ -123,10 +123,12 @@ pub fn generate_inline_relation_type_from_def(
123123 . cloned ( )
124124 . collect ( ) ;
125125
126- let field_ty = & field. ty ;
126+ // Convert SeaORM datetime types to chrono equivalents
127+ // This prevents users from needing to import sea_orm::prelude::DateTimeWithTimeZone
128+ let converted_ty = convert_type_with_chrono ( & field. ty , source_module_path) ;
127129 fields. push ( InlineField {
128130 name : field_ident. clone ( ) ,
129- ty : quote ! ( #field_ty ) ,
131+ ty : converted_ty ,
130132 attrs : kept_attrs,
131133 } ) ;
132134 }
@@ -150,6 +152,7 @@ pub fn generate_inline_relation_type_from_def(
150152pub fn generate_inline_relation_type_no_relations (
151153 parent_type_name : & syn:: Ident ,
152154 rel_info : & RelationFieldInfo ,
155+ source_module_path : & [ String ] ,
153156 schema_name_override : Option < & str > ,
154157) -> Option < InlineRelationType > {
155158 // Find the target model definition
@@ -160,6 +163,7 @@ pub fn generate_inline_relation_type_no_relations(
160163 generate_inline_relation_type_no_relations_from_def (
161164 parent_type_name,
162165 rel_info,
166+ source_module_path,
163167 schema_name_override,
164168 model_def,
165169 )
@@ -169,6 +173,7 @@ pub fn generate_inline_relation_type_no_relations(
169173pub fn generate_inline_relation_type_no_relations_from_def (
170174 parent_type_name : & syn:: Ident ,
171175 rel_info : & RelationFieldInfo ,
176+ source_module_path : & [ String ] ,
172177 schema_name_override : Option < & str > ,
173178 model_def : & str ,
174179) -> Option < InlineRelationType > {
@@ -214,10 +219,12 @@ pub fn generate_inline_relation_type_no_relations_from_def(
214219 . cloned ( )
215220 . collect ( ) ;
216221
217- let field_ty = & field. ty ;
222+ // Convert SeaORM datetime types to chrono equivalents
223+ // This prevents users from needing to import sea_orm::prelude::DateTimeWithTimeZone
224+ let converted_ty = convert_type_with_chrono ( & field. ty , source_module_path) ;
218225 fields. push ( InlineField {
219226 name : field_ident. clone ( ) ,
220- ty : quote ! ( #field_ty ) ,
227+ ty : converted_ty ,
221228 attrs : kept_attrs,
222229 } ) ;
223230 }
@@ -584,6 +591,7 @@ mod tests {
584591 let result = generate_inline_relation_type_no_relations_from_def (
585592 & parent_type_name,
586593 & rel_info,
594+ & [ ] ,
587595 None ,
588596 model_def,
589597 ) ;
@@ -626,6 +634,7 @@ mod tests {
626634 let result = generate_inline_relation_type_no_relations_from_def (
627635 & parent_type_name,
628636 & rel_info,
637+ & [ ] ,
629638 None ,
630639 model_def,
631640 ) ;
@@ -786,6 +795,7 @@ mod tests {
786795 let result = generate_inline_relation_type_no_relations_from_def (
787796 & parent_type_name,
788797 & rel_info,
798+ & [ ] ,
789799 Some ( "UserSchema" ) ,
790800 model_def,
791801 ) ;
@@ -906,7 +916,17 @@ pub struct Model {
906916 inline_type_info : None ,
907917 } ;
908918
909- let result = generate_inline_relation_type_no_relations ( & parent_type_name, & rel_info, None ) ;
919+ let source_module_path = vec ! [
920+ "crate" . to_string( ) ,
921+ "models" . to_string( ) ,
922+ "user" . to_string( ) ,
923+ ] ;
924+ let result = generate_inline_relation_type_no_relations (
925+ & parent_type_name,
926+ & rel_info,
927+ & source_module_path,
928+ None ,
929+ ) ;
910930
911931 // Restore original CARGO_MANIFEST_DIR
912932 // SAFETY: This is a test that runs single-threaded
@@ -1001,7 +1021,8 @@ pub struct Model {
10011021 inline_type_info : None ,
10021022 } ;
10031023
1004- let result = generate_inline_relation_type_no_relations ( & parent_type_name, & rel_info, None ) ;
1024+ let result =
1025+ generate_inline_relation_type_no_relations ( & parent_type_name, & rel_info, & [ ] , None ) ;
10051026
10061027 // Restore original CARGO_MANIFEST_DIR
10071028 // SAFETY: This is a test that runs single-threaded
@@ -1016,4 +1037,128 @@ pub struct Model {
10161037 // Should return None when file not found
10171038 assert ! ( result. is_none( ) ) ;
10181039 }
1040+
1041+ #[ test]
1042+ fn test_generate_inline_relation_type_converts_datetime_types ( ) {
1043+ // Test that DateTimeWithTimeZone is converted to vespera::chrono::DateTime<FixedOffset>
1044+ let parent_type_name = syn:: Ident :: new ( "MemoSchema" , proc_macro2:: Span :: call_site ( ) ) ;
1045+ let rel_info = RelationFieldInfo {
1046+ field_name : syn:: Ident :: new ( "user" , proc_macro2:: Span :: call_site ( ) ) ,
1047+ relation_type : "BelongsTo" . to_string ( ) ,
1048+ schema_path : quote ! ( super :: user:: Schema ) ,
1049+ is_optional : false ,
1050+ inline_type_info : None ,
1051+ } ;
1052+ let source_module_path = vec ! [
1053+ "crate" . to_string( ) ,
1054+ "models" . to_string( ) ,
1055+ "memo" . to_string( ) ,
1056+ ] ;
1057+
1058+ // Model with DateTimeWithTimeZone field AND circular reference
1059+ let model_def = r#"pub struct Model {
1060+ pub id: i32,
1061+ pub name: String,
1062+ pub created_at: DateTimeWithTimeZone,
1063+ pub memo: BelongsTo<memo::Entity>
1064+ }"# ;
1065+
1066+ let result = generate_inline_relation_type_from_def (
1067+ & parent_type_name,
1068+ & rel_info,
1069+ & source_module_path,
1070+ None ,
1071+ model_def,
1072+ ) ;
1073+ assert ! ( result. is_some( ) ) ;
1074+
1075+ let inline_type = result. unwrap ( ) ;
1076+ assert_eq ! ( inline_type. type_name. to_string( ) , "MemoSchema_User" ) ;
1077+
1078+ // Find created_at field and check its type was converted
1079+ let created_at_field = inline_type
1080+ . fields
1081+ . iter ( )
1082+ . find ( |f| f. name == "created_at" )
1083+ . expect ( "created_at field should exist" ) ;
1084+
1085+ let ty_str = created_at_field. ty . to_string ( ) ;
1086+ // Should be converted to vespera::chrono::DateTime<FixedOffset>
1087+ assert ! (
1088+ ty_str. contains( "vespera :: chrono :: DateTime" ) ,
1089+ "DateTimeWithTimeZone should be converted to vespera::chrono::DateTime, got: {}" ,
1090+ ty_str
1091+ ) ;
1092+ assert ! (
1093+ ty_str. contains( "FixedOffset" ) ,
1094+ "Should contain FixedOffset, got: {}" ,
1095+ ty_str
1096+ ) ;
1097+ }
1098+
1099+ #[ test]
1100+ fn test_generate_inline_relation_type_no_relations_converts_datetime_types ( ) {
1101+ // Test that DateTimeWithTimeZone is converted in no_relations variant too
1102+ let parent_type_name = syn:: Ident :: new ( "UserSchema" , proc_macro2:: Span :: call_site ( ) ) ;
1103+ let rel_info = RelationFieldInfo {
1104+ field_name : syn:: Ident :: new ( "memos" , proc_macro2:: Span :: call_site ( ) ) ,
1105+ relation_type : "HasMany" . to_string ( ) ,
1106+ schema_path : quote ! ( super :: memo:: Schema ) ,
1107+ is_optional : false ,
1108+ inline_type_info : None ,
1109+ } ;
1110+
1111+ // Model with DateTimeWithTimeZone field
1112+ let model_def = r#"pub struct Model {
1113+ pub id: i32,
1114+ pub title: String,
1115+ pub created_at: DateTimeWithTimeZone,
1116+ pub updated_at: Option<DateTimeWithTimeZone>,
1117+ pub user: BelongsTo<user::Entity>
1118+ }"# ;
1119+
1120+ let result = generate_inline_relation_type_no_relations_from_def (
1121+ & parent_type_name,
1122+ & rel_info,
1123+ & [ ] ,
1124+ None ,
1125+ model_def,
1126+ ) ;
1127+ assert ! ( result. is_some( ) ) ;
1128+
1129+ let inline_type = result. unwrap ( ) ;
1130+
1131+ // Find created_at field and check its type was converted
1132+ let created_at_field = inline_type
1133+ . fields
1134+ . iter ( )
1135+ . find ( |f| f. name == "created_at" )
1136+ . expect ( "created_at field should exist" ) ;
1137+
1138+ let ty_str = created_at_field. ty . to_string ( ) ;
1139+ assert ! (
1140+ ty_str. contains( "vespera :: chrono :: DateTime" ) ,
1141+ "DateTimeWithTimeZone should be converted, got: {}" ,
1142+ ty_str
1143+ ) ;
1144+
1145+ // Also check Option<DateTimeWithTimeZone>
1146+ let updated_at_field = inline_type
1147+ . fields
1148+ . iter ( )
1149+ . find ( |f| f. name == "updated_at" )
1150+ . expect ( "updated_at field should exist" ) ;
1151+
1152+ let updated_ty_str = updated_at_field. ty . to_string ( ) ;
1153+ assert ! (
1154+ updated_ty_str. contains( "Option" ) ,
1155+ "Should be Option type, got: {}" ,
1156+ updated_ty_str
1157+ ) ;
1158+ assert ! (
1159+ updated_ty_str. contains( "vespera :: chrono :: DateTime" ) ,
1160+ "Option<DateTimeWithTimeZone> should be converted, got: {}" ,
1161+ updated_ty_str
1162+ ) ;
1163+ }
10191164}
0 commit comments