@@ -13,9 +13,11 @@ use crate::util::{
1313use crate :: { indent_scope, OutputFile } ;
1414use convert_case:: { Case , Casing } ;
1515use spacetimedb_lib:: sats:: layout:: PrimitiveType ;
16+ use spacetimedb_lib:: sats:: AlgebraicTypeRef ;
1617use spacetimedb_primitives:: ColId ;
17- use spacetimedb_schema:: def:: { BTreeAlgorithm , IndexAlgorithm , ModuleDef , TableDef , TypeDef } ;
18- use spacetimedb_schema:: schema:: TableSchema ;
18+ use spacetimedb_schema:: def:: { BTreeAlgorithm , IndexAlgorithm , IndexDef , ModuleDef , TableDef , TypeDef , ViewDef } ;
19+ use spacetimedb_schema:: identifier:: Identifier ;
20+ use spacetimedb_schema:: schema:: { Schema , TableSchema } ;
1921use spacetimedb_schema:: type_for_generate:: {
2022 AlgebraicTypeDef , AlgebraicTypeUse , PlainEnumTypeDef , ProductTypeDef , SumTypeDef , TypespaceForGenerate ,
2123} ;
@@ -432,135 +434,164 @@ pub struct Csharp<'opts> {
432434 pub namespace : & ' opts str ,
433435}
434436
435- impl Lang for Csharp < ' _ > {
436- fn generate_table_file ( & self , module : & ModuleDef , table : & TableDef , schema : TableSchema ) -> OutputFile {
437- let mut output = CsharpAutogen :: new (
438- self . namespace ,
439- & [
440- "SpacetimeDB.BSATN" ,
441- "SpacetimeDB.ClientApi" ,
442- "System.Collections.Generic" ,
443- "System.Runtime.Serialization" ,
444- ] ,
445- false ,
446- ) ;
437+ fn gen_table_file (
438+ cs : & Csharp < ' _ > ,
439+ module : & ModuleDef ,
440+ schema : TableSchema ,
441+ product_type_ref : AlgebraicTypeRef ,
442+ table_name_ident : & Identifier ,
443+ index_defs : & [ IndexDef ] ,
444+ ) -> OutputFile {
445+ let mut output = CsharpAutogen :: new (
446+ cs. namespace ,
447+ & [
448+ "SpacetimeDB.BSATN" ,
449+ "SpacetimeDB.ClientApi" ,
450+ "System.Collections.Generic" ,
451+ "System.Runtime.Serialization" ,
452+ ] ,
453+ false ,
454+ ) ;
447455
448- writeln ! ( output, "public sealed partial class RemoteTables" ) ;
449- indented_block ( & mut output, |output| {
450- let csharp_table_name = table . name . deref ( ) . to_case ( Case :: Pascal ) ;
451- let csharp_table_class_name = csharp_table_name. clone ( ) + "Handle" ;
452- let table_type = type_ref_name ( module, table . product_type_ref ) ;
456+ writeln ! ( output, "public sealed partial class RemoteTables" ) ;
457+ indented_block ( & mut output, |output| {
458+ let csharp_table_name = table_name_ident . deref ( ) . to_case ( Case :: Pascal ) ;
459+ let csharp_table_class_name = csharp_table_name. clone ( ) + "Handle" ;
460+ let table_type = type_ref_name ( module, product_type_ref) ;
453461
462+ writeln ! (
463+ output,
464+ "public sealed class {csharp_table_class_name} : RemoteTableHandle<EventContext, {table_type}>"
465+ ) ;
466+ indented_block ( output, |output| {
454467 writeln ! (
455468 output,
456- "public sealed class {csharp_table_class_name} : RemoteTableHandle<EventContext, {table_type}>"
469+ "protected override string RemoteTableName => \" {}\" ;" ,
470+ table_name_ident
457471 ) ;
458- indented_block ( output, |output| {
459- writeln ! (
460- output,
461- "protected override string RemoteTableName => \" {}\" ;" ,
462- table. name
463- ) ;
464- writeln ! ( output) ;
472+ writeln ! ( output) ;
465473
466- // If this is a table, we want to generate event accessor and indexes
467- let product_type = module. typespace_for_generate ( ) [ table. product_type_ref ]
468- . as_product ( )
469- . unwrap ( ) ;
470-
471- let mut index_names = Vec :: new ( ) ;
472-
473- for idx in iter_indexes ( table) {
474- let Some ( accessor_name) = idx. accessor_name . as_ref ( ) else {
475- // If there is no accessor name, we shouldn't generate a client-side index accessor.
476- continue ;
477- } ;
478-
479- match & idx. algorithm {
480- IndexAlgorithm :: BTree ( BTreeAlgorithm { columns } ) => {
481- let get_csharp_field_name_and_type = |col_pos : ColId | {
482- let ( field_name, field_type) = & product_type. elements [ col_pos. idx ( ) ] ;
483- let csharp_field_name_pascal = field_name. deref ( ) . to_case ( Case :: Pascal ) ;
484- let csharp_field_type = ty_fmt ( module, field_type) ;
485- ( csharp_field_name_pascal, csharp_field_type)
486- } ;
487-
488- let ( row_to_key, key_type) = match columns. as_singleton ( ) {
489- Some ( col_pos) => {
490- let ( field_name, field_type) = get_csharp_field_name_and_type ( col_pos) ;
491- ( format ! ( "row.{field_name}" ) , field_type. to_string ( ) )
492- }
493- None => {
494- let mut key_accessors = Vec :: new ( ) ;
495- let mut key_type_elems = Vec :: new ( ) ;
496- for ( field_name, field_type) in columns. iter ( ) . map ( get_csharp_field_name_and_type) {
497- key_accessors. push ( format ! ( "row.{field_name}" ) ) ;
498- key_type_elems. push ( format ! ( "{field_type} {field_name}" ) ) ;
499- }
500- (
501- format ! ( "({})" , key_accessors. join( ", " ) ) ,
502- format ! ( "({})" , key_type_elems. join( ", " ) ) ,
503- )
474+ // If this is a table, we want to generate event accessor and indexes
475+ let product_type = module. typespace_for_generate ( ) [ product_type_ref] . as_product ( ) . unwrap ( ) ;
476+
477+ let mut index_names = Vec :: new ( ) ;
478+
479+ for idx in iter_indexes ( index_defs) {
480+ let Some ( accessor_name) = idx. accessor_name . as_ref ( ) else {
481+ // If there is no accessor name, we shouldn't generate a client-side index accessor.
482+ continue ;
483+ } ;
484+
485+ match & idx. algorithm {
486+ IndexAlgorithm :: BTree ( BTreeAlgorithm { columns } ) => {
487+ let get_csharp_field_name_and_type = |col_pos : ColId | {
488+ let ( field_name, field_type) = & product_type. elements [ col_pos. idx ( ) ] ;
489+ let csharp_field_name_pascal = field_name. deref ( ) . to_case ( Case :: Pascal ) ;
490+ let csharp_field_type = ty_fmt ( module, field_type) ;
491+ ( csharp_field_name_pascal, csharp_field_type)
492+ } ;
493+
494+ let ( row_to_key, key_type) = match columns. as_singleton ( ) {
495+ Some ( col_pos) => {
496+ let ( field_name, field_type) = get_csharp_field_name_and_type ( col_pos) ;
497+ ( format ! ( "row.{field_name}" ) , field_type. to_string ( ) )
498+ }
499+ None => {
500+ let mut key_accessors = Vec :: new ( ) ;
501+ let mut key_type_elems = Vec :: new ( ) ;
502+ for ( field_name, field_type) in columns. iter ( ) . map ( get_csharp_field_name_and_type) {
503+ key_accessors. push ( format ! ( "row.{field_name}" ) ) ;
504+ key_type_elems. push ( format ! ( "{field_type} {field_name}" ) ) ;
504505 }
505- } ;
506-
507- let csharp_index_name = accessor_name . deref ( ) . to_case ( Case :: Pascal ) ;
508-
509- let mut csharp_index_class_name = csharp_index_name . clone ( ) ;
510- let csharp_index_base_class_name = if schema . is_unique ( columns ) {
511- csharp_index_class_name += "UniqueIndex" ;
512- "UniqueIndexBase"
513- } else {
514- csharp_index_class_name += "Index" ;
515- "BTreeIndexBase"
516- } ;
517-
518- writeln ! ( output , "public sealed class {csharp_index_class_name} : {csharp_index_base_class_name}<{key_type}>" ) ;
519- indented_block ( output , |output| {
520- writeln ! (
521- output ,
522- "protected override {key_type} GetKey({table_type} row) => {row_to_key};"
523- ) ;
524- writeln ! ( output) ;
525- writeln ! ( output , "public {csharp_index_class_name}({csharp_table_class_name} table) : base(table) {{ }}" ) ;
526- } ) ;
527- writeln ! ( output ) ;
528- writeln ! ( output , "public readonly {csharp_index_class_name} {csharp_index_name};" ) ;
506+ (
507+ format ! ( "({})" , key_accessors . join ( ", " ) ) ,
508+ format ! ( "({})" , key_type_elems . join ( ", " ) ) ,
509+ )
510+ }
511+ } ;
512+
513+ let csharp_index_name = accessor_name . deref ( ) . to_case ( Case :: Pascal ) ;
514+
515+ let mut csharp_index_class_name = csharp_index_name . clone ( ) ;
516+ let csharp_index_base_class_name = if schema . is_unique ( columns ) {
517+ csharp_index_class_name += "UniqueIndex" ;
518+ "UniqueIndexBase"
519+ } else {
520+ csharp_index_class_name += "Index" ;
521+ "BTreeIndexBase"
522+ } ;
523+
524+ writeln ! ( output , "public sealed class {csharp_index_class_name} : {csharp_index_base_class_name}<{key_type}>" ) ;
525+ indented_block ( output, |output| {
526+ writeln ! (
527+ output ,
528+ "protected override {key_type} GetKey({table_type} row) => {row_to_key};"
529+ ) ;
529530 writeln ! ( output) ;
531+ writeln ! (
532+ output,
533+ "public {csharp_index_class_name}({csharp_table_class_name} table) : base(table) {{ }}"
534+ ) ;
535+ } ) ;
536+ writeln ! ( output) ;
537+ writeln ! ( output, "public readonly {csharp_index_class_name} {csharp_index_name};" ) ;
538+ writeln ! ( output) ;
530539
531- index_names. push ( csharp_index_name) ;
532- }
533- _ => todo ! ( ) ,
540+ index_names. push ( csharp_index_name) ;
534541 }
542+ _ => todo ! ( ) ,
543+ }
544+ }
545+
546+ writeln ! (
547+ output,
548+ "internal {csharp_table_class_name}(DbConnection conn) : base(conn)"
549+ ) ;
550+ indented_block ( output, |output| {
551+ for csharp_index_name in & index_names {
552+ writeln ! ( output, "{csharp_index_name} = new(this);" ) ;
535553 }
554+ } ) ;
536555
556+ if let Some ( primary_col_index) = schema. pk ( ) {
557+ writeln ! ( output) ;
537558 writeln ! (
538559 output,
539- "internal {csharp_table_class_name}(DbConnection conn) : base(conn)"
560+ "protected override object GetPrimaryKey({table_type} row) => row.{col_name_pascal_case};" ,
561+ col_name_pascal_case = primary_col_index. col_name. deref( ) . to_case( Case :: Pascal )
540562 ) ;
541- indented_block ( output, |output| {
542- for csharp_index_name in & index_names {
543- writeln ! ( output, "{csharp_index_name} = new(this);" ) ;
544- }
545- } ) ;
546-
547- if let Some ( primary_col_index) = schema. pk ( ) {
548- writeln ! ( output) ;
549- writeln ! (
550- output,
551- "protected override object GetPrimaryKey({table_type} row) => row.{col_name_pascal_case};" ,
552- col_name_pascal_case = primary_col_index. col_name. deref( ) . to_case( Case :: Pascal )
553- ) ;
554- }
555- } ) ;
556- writeln ! ( output) ;
557- writeln ! ( output, "public readonly {csharp_table_class_name} {csharp_table_name};" ) ;
563+ }
558564 } ) ;
565+ writeln ! ( output) ;
566+ writeln ! ( output, "public readonly {csharp_table_class_name} {csharp_table_name};" ) ;
567+ } ) ;
559568
560- OutputFile {
561- filename : format ! ( "Tables/{}.g.cs" , table. name. deref( ) . to_case( Case :: Pascal ) ) ,
562- code : output. into_inner ( ) ,
563- }
569+ OutputFile {
570+ filename : format ! ( "Tables/{}.g.cs" , table_name_ident. deref( ) . to_case( Case :: Pascal ) ) ,
571+ code : output. into_inner ( ) ,
572+ }
573+ }
574+
575+ impl Lang for Csharp < ' _ > {
576+ fn generate_table_file ( & self , module : & ModuleDef , table : & TableDef ) -> OutputFile {
577+ let table_schema = TableSchema :: from_module_def ( module, table, ( ) , 0 . into ( ) )
578+ . validated ( )
579+ . expect ( "Failed to generate table due to validation errors" ) ;
580+ gen_table_file (
581+ self ,
582+ module,
583+ table_schema,
584+ table. product_type_ref ,
585+ & table. name ,
586+ & table. indexes . values ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ,
587+ )
588+ }
589+
590+ fn generate_view_file ( & self , module : & ModuleDef , view : & ViewDef ) -> OutputFile {
591+ let table_schema = TableSchema :: from_view_def_for_codegen ( module, view)
592+ . validated ( )
593+ . expect ( "Failed to generate table due to validation errors" ) ;
594+ gen_table_file ( self , module, table_schema, view. product_type_ref , & view. name , & [ ] )
564595 }
565596
566597 fn generate_type_files ( & self , module : & ModuleDef , typ : & TypeDef ) -> Vec < OutputFile > {
0 commit comments