@@ -186,13 +186,40 @@ pub fn find_struct_by_name_in_all_files(
186186 . or_else ( || hint_lower. strip_suffix ( "request" ) )
187187 . unwrap_or ( & hint_lower) ;
188188
189- // Find files whose name contains the prefix
189+ // Normalize prefix: remove underscores for comparison
190+ // This allows "AdminUserSchema" (prefix "adminuser") to match "admin_user.rs"
191+ let prefix_normalized = prefix. replace ( '_' , "" ) ;
192+
193+ // First, try exact filename match (normalized)
194+ // e.g., "admin_user.rs" normalized to "adminuser" matches prefix "adminuser"
195+ let exact_match: Vec < _ > = found_structs
196+ . iter ( )
197+ . filter ( |( path, _) | {
198+ path. file_stem ( )
199+ . and_then ( |s| s. to_str ( ) )
200+ . is_some_and ( |name| {
201+ name. to_lowercase ( ) . replace ( '_' , "" ) == prefix_normalized
202+ } )
203+ } )
204+ . collect ( ) ;
205+
206+ if exact_match. len ( ) == 1 {
207+ let ( path, metadata) = exact_match[ 0 ] ;
208+ let module_path = file_path_to_module_path ( path, src_dir) ;
209+ return Some ( ( metadata. clone ( ) , module_path) ) ;
210+ }
211+
212+ // Fallback: Find files whose normalized name contains the prefix
190213 let matching: Vec < _ > = found_structs
191214 . into_iter ( )
192215 . filter ( |( path, _) | {
193216 path. file_stem ( )
194217 . and_then ( |s| s. to_str ( ) )
195- . is_some_and ( |name| name. to_lowercase ( ) . contains ( prefix) )
218+ . is_some_and ( |name| {
219+ name. to_lowercase ( )
220+ . replace ( '_' , "" )
221+ . contains ( & prefix_normalized)
222+ } )
196223 } )
197224 . collect ( ) ;
198225
@@ -763,6 +790,59 @@ pub struct Target { pub id: i32 }
763790 ) ;
764791 }
765792
793+ #[ test]
794+ #[ serial]
795+ fn test_find_struct_disambiguation_snake_case_filename ( ) {
796+ // Tests: CamelCase schema name matches snake_case filename
797+ // e.g., "AdminUserSchema" should match "admin_user.rs"
798+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
799+ let src_dir = temp_dir. path ( ) ;
800+
801+ std:: fs:: create_dir ( src_dir. join ( "models" ) ) . unwrap ( ) ;
802+ // Create admin_user.rs with Model
803+ std:: fs:: write (
804+ src_dir. join ( "models" ) . join ( "admin_user.rs" ) ,
805+ "pub struct Model { pub id: i32, pub role: String }" ,
806+ )
807+ . unwrap ( ) ;
808+ // Create regular_user.rs with Model
809+ std:: fs:: write (
810+ src_dir. join ( "models" ) . join ( "regular_user.rs" ) ,
811+ "pub struct Model { pub id: i32, pub name: String }" ,
812+ )
813+ . unwrap ( ) ;
814+
815+ // With hint "AdminUserSchema" - should find admin_user.rs
816+ // "AdminUserSchema" -> prefix "adminuser" -> matches "admin_user.rs" (normalized: "adminuser")
817+ let result = find_struct_by_name_in_all_files ( src_dir, "Model" , Some ( "AdminUserSchema" ) ) ;
818+ assert ! (
819+ result. is_some( ) ,
820+ "AdminUserSchema hint should match admin_user.rs"
821+ ) ;
822+ let ( metadata, module_path) = result. unwrap ( ) ;
823+ assert ! (
824+ metadata. definition. contains( "role" ) ,
825+ "Should be admin_user Model with role field"
826+ ) ;
827+ assert ! (
828+ module_path. contains( & "admin_user" . to_string( ) ) ,
829+ "Module path should contain 'admin_user'"
830+ ) ;
831+
832+ // With hint "RegularUserSchema" - should find regular_user.rs
833+ let result_regular =
834+ find_struct_by_name_in_all_files ( src_dir, "Model" , Some ( "RegularUserSchema" ) ) ;
835+ assert ! (
836+ result_regular. is_some( ) ,
837+ "RegularUserSchema hint should match regular_user.rs"
838+ ) ;
839+ let ( metadata_regular, _) = result_regular. unwrap ( ) ;
840+ assert ! (
841+ metadata_regular. definition. contains( "name" ) ,
842+ "Should be regular_user Model with name field"
843+ ) ;
844+ }
845+
766846 // ============================================================
767847 // Coverage tests for find_struct_from_schema_path
768848 // ============================================================
0 commit comments