@@ -24,26 +24,27 @@ pub fn detect_circular_fields(
2424 source_module_path : & [ String ] ,
2525 related_schema_def : & str ,
2626) -> Vec < String > {
27- let mut circular_fields = Vec :: new ( ) ;
28-
2927 // Parse the related schema definition
3028 let Ok ( parsed) = syn:: parse_str :: < syn:: ItemStruct > ( related_schema_def) else {
31- return circular_fields ;
29+ return Vec :: new ( ) ;
3230 } ;
3331
3432 // Get the source module name (e.g., "memo" from ["crate", "models", "memo"])
3533 let source_module = source_module_path. last ( ) . map ( |s| s. as_str ( ) ) . unwrap_or ( "" ) ;
3634
37- if let syn:: Fields :: Named ( fields_named) = & parsed. fields {
38- for field in & fields_named. named {
39- let Some ( field_ident) = & field. ident else {
40- continue ;
41- } ;
35+ let syn:: Fields :: Named ( fields_named) = & parsed. fields else {
36+ return Vec :: new ( ) ;
37+ } ;
38+
39+ fields_named
40+ . named
41+ . iter ( )
42+ . filter_map ( |field| {
43+ let field_ident = field. ident . as_ref ( ) ?;
4244 let field_name = field_ident. to_string ( ) ;
4345
4446 // Check if this field's type references the source schema
45- let field_ty = & field. ty ;
46- let ty_str = quote ! ( #field_ty) . to_string ( ) ;
47+ let ty_str = quote ! ( #field. ty) . to_string ( ) ;
4748
4849 // Normalize whitespace: quote!() produces "foo :: bar" instead of "foo::bar"
4950 // Remove all whitespace to make pattern matching reliable
@@ -52,7 +53,7 @@ pub fn detect_circular_fields(
5253 // SKIP HasMany relations - they are excluded by default from schemas,
5354 // so they don't create actual circular references in the output
5455 if ty_str_normalized. contains ( "HasMany<" ) {
55- continue ;
56+ return None ;
5657 }
5758
5859 // Check for BelongsTo/HasOne patterns that reference the source:
@@ -68,13 +69,9 @@ pub fn detect_circular_fields(
6869 || ty_str_normalized
6970 . contains ( & format ! ( "{}Schema" , capitalize_first( source_module) ) ) ) ;
7071
71- if is_circular {
72- circular_fields. push ( field_name) ;
73- }
74- }
75- }
76-
77- circular_fields
72+ is_circular. then_some ( field_name)
73+ } )
74+ . collect ( )
7875}
7976
8077/// Check if a Model has any BelongsTo or HasOne relations (FK-based relations).
@@ -112,32 +109,38 @@ pub fn is_circular_relation_required(related_model_def: &str, circular_field_nam
112109 return false ;
113110 } ;
114111
115- if let syn:: Fields :: Named ( fields_named) = & parsed. fields {
116- for field in & fields_named. named {
117- let Some ( field_ident) = & field. ident else {
118- continue ;
119- } ;
120- if * field_ident != circular_field_name {
121- continue ;
122- }
112+ let syn:: Fields :: Named ( fields_named) = & parsed. fields else {
113+ return false ;
114+ } ;
123115
124- // Check if this is a HasOne/BelongsTo with required FK
125- let ty_str = quote ! ( #field. ty) . to_string ( ) . replace ( ' ' , "" ) ;
126- if ty_str. contains ( "HasOne<" ) || ty_str. contains ( "BelongsTo<" ) {
127- // Check FK field optionality
128- let fk_field = extract_belongs_to_from_field ( & field. attrs ) ;
129- if let Some ( fk) = fk_field {
130- // Find FK field and check if it's Option
131- for f in & fields_named. named {
132- if f. ident . as_ref ( ) . map ( |i| i. to_string ( ) ) == Some ( fk. clone ( ) ) {
133- return !is_option_type ( & f. ty ) ;
134- }
135- }
136- }
137- }
138- }
116+ // Find the circular field by name
117+ let Some ( field) = fields_named. named . iter ( ) . find ( |f| {
118+ f. ident
119+ . as_ref ( )
120+ . map ( |i| i == circular_field_name)
121+ . unwrap_or ( false )
122+ } ) else {
123+ return false ;
124+ } ;
125+
126+ // Check if this is a HasOne/BelongsTo with required FK
127+ let ty_str = quote ! ( #field. ty) . to_string ( ) . replace ( ' ' , "" ) ;
128+ if !ty_str. contains ( "HasOne<" ) && !ty_str. contains ( "BelongsTo<" ) {
129+ return false ;
139130 }
140- false
131+
132+ // Check FK field optionality
133+ let Some ( fk) = extract_belongs_to_from_field ( & field. attrs ) else {
134+ return false ;
135+ } ;
136+
137+ // Find FK field and check if it's Option
138+ fields_named
139+ . named
140+ . iter ( )
141+ . find ( |f| f. ident . as_ref ( ) . map ( |i| i. to_string ( ) ) == Some ( fk. clone ( ) ) )
142+ . map ( |f| !is_option_type ( & f. ty ) )
143+ . unwrap_or ( false )
141144}
142145
143146/// Generate a default value for a SeaORM relation field in inline construction.
0 commit comments