Skip to content

Commit 2c4343a

Browse files
extend trait
1 parent b3dc60b commit 2c4343a

File tree

7 files changed

+484
-445
lines changed

7 files changed

+484
-445
lines changed

crates/codegen/src/csharp.rs

Lines changed: 145 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ use crate::util::{
1313
use crate::{indent_scope, OutputFile};
1414
use convert_case::{Case, Casing};
1515
use spacetimedb_lib::sats::layout::PrimitiveType;
16+
use spacetimedb_lib::sats::AlgebraicTypeRef;
1617
use 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};
1921
use 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> {

crates/codegen/src/lib.rs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use spacetimedb_schema::def::{ModuleDef, ReducerDef, TableDef, TypeDef};
2-
use spacetimedb_schema::schema::{Schema, TableSchema};
1+
use spacetimedb_schema::def::{ModuleDef, ReducerDef, TableDef, TypeDef, ViewDef};
32

43
mod code_indenter;
54
pub mod csharp;
@@ -15,25 +14,9 @@ pub use self::unrealcpp::UnrealCpp;
1514
pub use util::AUTO_GENERATED_PREFIX;
1615

1716
pub fn generate(module: &ModuleDef, lang: &dyn Lang) -> Vec<OutputFile> {
18-
let tables = module.tables().map(|table_def| {
19-
(
20-
table_def,
21-
TableSchema::from_module_def(module, table_def, (), 0.into())
22-
.validated()
23-
.expect("Failed to generate table due to validation errors"),
24-
)
25-
});
26-
let views = module.views().map(|view_def| {
27-
(
28-
TableDef::from(view_def.clone()),
29-
TableSchema::from_view_def_for_codegen(module, view_def)
30-
.validated()
31-
.expect("Failed to generate table due to validation errors"),
32-
)
33-
});
3417
itertools::chain!(
35-
tables.map(|(table_def, table_schema)| lang.generate_table_file(module, table_def, table_schema)),
36-
views.map(|(table_def, table_schema)| lang.generate_table_file(module, &table_def, table_schema)),
18+
module.tables().map(|tbl| lang.generate_table_file(module, tbl)),
19+
module.views().map(|view| lang.generate_view_file(module, view)),
3720
module.types().flat_map(|typ| lang.generate_type_files(module, typ)),
3821
util::iter_reducers(module).map(|reducer| lang.generate_reducer_file(module, reducer)),
3922
lang.generate_global_files(module),
@@ -47,7 +30,8 @@ pub struct OutputFile {
4730
}
4831

4932
pub trait Lang {
50-
fn generate_table_file(&self, module: &ModuleDef, tbl: &TableDef, schema: TableSchema) -> OutputFile;
33+
fn generate_table_file(&self, module: &ModuleDef, table: &TableDef) -> OutputFile;
34+
fn generate_view_file(&self, module: &ModuleDef, table: &ViewDef) -> OutputFile;
5135
fn generate_type_files(&self, module: &ModuleDef, typ: &TypeDef) -> Vec<OutputFile>;
5236
fn generate_reducer_file(&self, module: &ModuleDef, reducer: &ReducerDef) -> OutputFile;
5337
fn generate_global_files(&self, module: &ModuleDef) -> Vec<OutputFile>;

0 commit comments

Comments
 (0)