@@ -62,6 +62,24 @@ pub fn normalize_module_name(name: &str) -> String {
6262 result
6363}
6464
65+ /// Well-known default schemas that don't need a prefix in filenames.
66+ const DEFAULT_SCHEMAS : & [ & str ] = & [ "public" , "main" , "dbo" ] ;
67+
68+ /// Returns true if the schema is a well-known default (public, main, dbo).
69+ pub fn is_default_schema ( schema : & str ) -> bool {
70+ DEFAULT_SCHEMAS . contains ( & schema)
71+ }
72+
73+ /// Build a module name, prefixing with schema when there are multiple schemas
74+ /// and the schema is not a well-known default.
75+ pub fn build_module_name ( schema_name : & str , table_name : & str , has_multiple_schemas : bool ) -> String {
76+ if !has_multiple_schemas || DEFAULT_SCHEMAS . contains ( & schema_name) {
77+ normalize_module_name ( table_name)
78+ } else {
79+ normalize_module_name ( & format ! ( "{}_{}" , schema_name, table_name) )
80+ }
81+ }
82+
6583/// A generated code file with its content and required imports.
6684#[ derive( Debug , Clone ) ]
6785pub struct GeneratedFile {
@@ -81,13 +99,23 @@ pub fn generate(
8199) -> Vec < GeneratedFile > {
82100 let mut files = Vec :: new ( ) ;
83101
102+ // Detect if multiple schemas are present
103+ let mut schemas = BTreeSet :: new ( ) ;
104+ for t in & schema_info. tables {
105+ schemas. insert ( t. schema_name . as_str ( ) ) ;
106+ }
107+ for v in & schema_info. views {
108+ schemas. insert ( v. schema_name . as_str ( ) ) ;
109+ }
110+ let has_multiple_schemas = schemas. len ( ) > 1 ;
111+
84112 // Generate struct files for each table
85113 for table in & schema_info. tables {
86114 let ( tokens, imports) =
87115 struct_gen:: generate_struct ( table, db_kind, schema_info, extra_derives, type_overrides, false ) ;
88116 let imports = filter_imports ( & imports, single_file) ;
89117 let code = format_tokens_with_imports ( & tokens, & imports) ;
90- let module_name = normalize_module_name ( & table. name ) ;
118+ let module_name = build_module_name ( & table. schema_name , & table . name , has_multiple_schemas ) ;
91119 let origin = format ! ( "Table: {}.{}" , table. schema_name, table. name) ;
92120 files. push ( GeneratedFile {
93121 filename : format ! ( "{}.rs" , module_name) ,
@@ -102,7 +130,7 @@ pub fn generate(
102130 struct_gen:: generate_struct ( view, db_kind, schema_info, extra_derives, type_overrides, true ) ;
103131 let imports = filter_imports ( & imports, single_file) ;
104132 let code = format_tokens_with_imports ( & tokens, & imports) ;
105- let module_name = normalize_module_name ( & view. name ) ;
133+ let module_name = build_module_name ( & view. schema_name , & view . name , has_multiple_schemas ) ;
106134 let origin = format ! ( "View: {}.{}" , view. schema_name, view. name) ;
107135 files. push ( GeneratedFile {
108136 filename : format ! ( "{}.rs" , module_name) ,
@@ -417,6 +445,55 @@ mod tests {
417445 assert_eq ! ( normalize_module_name( "a__b__c" ) , "a_b_c" ) ;
418446 }
419447
448+ // ========== build_module_name ==========
449+
450+ #[ test]
451+ fn test_build_single_schema_no_prefix ( ) {
452+ assert_eq ! ( build_module_name( "public" , "users" , false ) , "users" ) ;
453+ }
454+
455+ #[ test]
456+ fn test_build_multi_schema_default_no_prefix ( ) {
457+ assert_eq ! ( build_module_name( "public" , "users" , true ) , "users" ) ;
458+ }
459+
460+ #[ test]
461+ fn test_build_multi_schema_non_default_prefixed ( ) {
462+ assert_eq ! ( build_module_name( "billing" , "users" , true ) , "billing_users" ) ;
463+ }
464+
465+ #[ test]
466+ fn test_build_multi_schema_dbo_no_prefix ( ) {
467+ assert_eq ! ( build_module_name( "dbo" , "users" , true ) , "users" ) ;
468+ }
469+
470+ #[ test]
471+ fn test_build_multi_schema_main_no_prefix ( ) {
472+ assert_eq ! ( build_module_name( "main" , "users" , true ) , "users" ) ;
473+ }
474+
475+ #[ test]
476+ fn test_build_normalizes_double_underscore ( ) {
477+ assert_eq ! ( build_module_name( "billing" , "agent__connector" , true ) , "billing_agent_connector" ) ;
478+ }
479+
480+ // ========== is_default_schema ==========
481+
482+ #[ test]
483+ fn test_default_schema_public ( ) {
484+ assert ! ( is_default_schema( "public" ) ) ;
485+ }
486+
487+ #[ test]
488+ fn test_default_schema_main ( ) {
489+ assert ! ( is_default_schema( "main" ) ) ;
490+ }
491+
492+ #[ test]
493+ fn test_non_default_schema ( ) {
494+ assert ! ( !is_default_schema( "billing" ) ) ;
495+ }
496+
420497 // ========== imports_for_derives ==========
421498
422499 #[ test]
@@ -851,6 +928,39 @@ mod tests {
851928 assert ! ( files[ 0 ] . code. contains( "Option<String>" ) ) ;
852929 }
853930
931+ #[ test]
932+ fn test_generate_multi_schema_prefixes_non_default ( ) {
933+ let schema = SchemaInfo {
934+ tables : vec ! [
935+ make_table( "users" , vec![ make_col( "id" , "int4" ) ] ) ,
936+ TableInfo {
937+ schema_name: "billing" . to_string( ) ,
938+ name: "users" . to_string( ) ,
939+ columns: vec![ make_col( "id" , "int4" ) ] ,
940+ } ,
941+ ] ,
942+ ..Default :: default ( )
943+ } ;
944+ let files = generate ( & schema, DatabaseKind :: Postgres , & [ ] , & HashMap :: new ( ) , false ) ;
945+ let filenames: Vec < _ > = files. iter ( ) . map ( |f| f. filename . as_str ( ) ) . collect ( ) ;
946+ assert ! ( filenames. contains( & "users.rs" ) ) ;
947+ assert ! ( filenames. contains( & "billing_users.rs" ) ) ;
948+ }
949+
950+ #[ test]
951+ fn test_generate_single_schema_no_prefix ( ) {
952+ let schema = SchemaInfo {
953+ tables : vec ! [
954+ make_table( "users" , vec![ make_col( "id" , "int4" ) ] ) ,
955+ make_table( "posts" , vec![ make_col( "id" , "int4" ) ] ) ,
956+ ] ,
957+ ..Default :: default ( )
958+ } ;
959+ let files = generate ( & schema, DatabaseKind :: Postgres , & [ ] , & HashMap :: new ( ) , false ) ;
960+ assert_eq ! ( files[ 0 ] . filename, "users.rs" ) ;
961+ assert_eq ! ( files[ 1 ] . filename, "posts.rs" ) ;
962+ }
963+
854964 #[ test]
855965 fn test_generate_view_single_file_mode ( ) {
856966 let schema = SchemaInfo {
0 commit comments