Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
07ee5d6
Updated to regex for namespace troubles
JasonAtClockwork Nov 15, 2025
82a3eaf
Fix linting issue
JasonAtClockwork Nov 15, 2025
55e4c93
Reviewed and we can remove the hacky global scope add for return type…
JasonAtClockwork Nov 17, 2025
599229d
Merge branch 'master' into jlarabie/csharp-view-ns
jdetter Nov 18, 2025
594c4d1
Added a Views tests to the C# regression tests
rekhoff Nov 18, 2025
7a9c0fe
client codegen fixes for views
joshua-spacetime Nov 18, 2025
988e13b
regen client bindings
joshua-spacetime Nov 18, 2025
a6ebefd
Merge branch 'joshua/fix/client-bindings-for-views' into jlarabie/csh…
rekhoff Nov 18, 2025
0188ee2
Merge branch 'jlarabie/csharp-view-ns' into rekhoff/csharp_replicatio…
rekhoff Nov 18, 2025
837a6a2
Updates the Views test of the C# regression tests
rekhoff Nov 19, 2025
4f7296d
Minor formatting update
rekhoff Nov 19, 2025
9a10dc1
fix client codegen for views
joshua-spacetime Nov 19, 2025
1afba75
Minor fixes
rekhoff Nov 19, 2025
056e3b6
Merge branch 'joshua/fix/client-bindings-for-views' into jlarabie/csh…
rekhoff Nov 19, 2025
8698dd3
Merge branch 'jlarabie/csharp-view-ns' into rekhoff/csharp_replicatio…
rekhoff Nov 19, 2025
cd36a95
Update sdks/csharp/examples~/regression-tests/client/Program.cs
rekhoff Nov 19, 2025
6340f61
Fixed up the C# module bindings for Views
JasonAtClockwork Nov 19, 2025
7022772
Merge branch 'master' into rekhoff/csharp_replication_test_with_views
joshua-spacetime Nov 20, 2025
ad7931e
revert sdk-test changes
joshua-spacetime Nov 20, 2025
2de6f15
revert rust codegen change
joshua-spacetime Nov 20, 2025
d418bb5
Update ExtraCompilationErrors.verified.txt
rekhoff Nov 20, 2025
a5adf19
Merge branch 'master' into rekhoff/csharp_replication_test_with_views
rekhoff Nov 20, 2025
773ecb0
Addressing some comments
rekhoff Nov 20, 2025
74d9581
Merge branch 'rekhoff/csharp_replication_test_with_views' of https://…
rekhoff Nov 20, 2025
3e6278e
Updated whitespace of client to match tests
rekhoff Nov 20, 2025
75648b0
Updated the codegen to have identical generation for RegisterView + V…
JasonAtClockwork Nov 20, 2025
d257d90
Update crates/bindings-csharp/Codegen/Module.cs
JasonAtClockwork Nov 20, 2025
5a137c5
Update crates/bindings-csharp/Codegen/Module.cs
JasonAtClockwork Nov 20, 2025
a64461e
Added more equality checks for the Views data
JasonAtClockwork Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions crates/bindings-csharp/Codegen/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -997,10 +997,7 @@ public string GenerateViewDef(uint Index) =>
IsPublic: {{{IsPublic.ToString().ToLower()}}},
IsAnonymous: {{{IsAnonymous.ToString().ToLower()}}},
Params: [{{{MemberDeclaration.GenerateDefs(Parameters)}}}],
ReturnType: new {{{ReturnType.BSATNName.Replace(
"Module.",
"global::Module."
)}}}().GetAlgebraicType(registrar)
ReturnType: new {{{ReturnType.BSATNName}}}().GetAlgebraicType(registrar)
);
""";

Expand Down
8 changes: 4 additions & 4 deletions crates/codegen/src/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::ops::Deref;
use super::code_indenter::CodeIndenter;
use super::Lang;
use crate::util::{
collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, print_auto_generated_file_comment,
print_auto_generated_version_comment, type_ref_name,
collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_table_names_and_types,
print_auto_generated_file_comment, print_auto_generated_version_comment, type_ref_name,
};
use crate::{indent_scope, OutputFile};
use convert_case::{Case, Casing};
Expand Down Expand Up @@ -745,11 +745,11 @@ impl Lang for Csharp<'_> {
indented_block(&mut output, |output| {
writeln!(output, "public RemoteTables(DbConnection conn)");
indented_block(output, |output| {
for table in iter_tables(module) {
for (table_name, _) in iter_table_names_and_types(module) {
writeln!(
output,
"AddTable({} = new(conn));",
table.name.deref().to_case(Case::Pascal)
table_name.deref().to_case(Case::Pascal)
);
}
});
Expand Down
74 changes: 41 additions & 33 deletions crates/codegen/src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use super::code_indenter::{CodeIndenter, Indenter};
use super::util::{collect_case, iter_reducers, print_lines, type_ref_name};
use super::Lang;
use crate::util::{
iter_procedures, iter_tables, iter_types, iter_unique_cols, print_auto_generated_file_comment,
print_auto_generated_version_comment,
iter_procedures, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols, iter_views,
print_auto_generated_file_comment, print_auto_generated_version_comment,
};
use crate::OutputFile;
use convert_case::{Case, Casing};
Expand Down Expand Up @@ -930,12 +930,13 @@ fn reducer_flags_trait_name(reducer: &ReducerDef) -> String {
format!("set_flags_for_{}", reducer_function_name(reducer))
}

/// Iterate over all of the Rust `mod`s for types, reducers and tables in the `module`.
/// Iterate over all of the Rust `mod`s for types, reducers, views, and tables in the `module`.
fn iter_module_names(module: &ModuleDef) -> impl Iterator<Item = String> + '_ {
itertools::chain!(
iter_types(module).map(|ty| type_module_name(&ty.name)),
iter_reducers(module).map(|r| reducer_module_name(&r.name)),
iter_tables(module).map(|tbl| table_module_name(&tbl.name)),
iter_views(module).map(|view| table_module_name(&view.name)),
iter_procedures(module).map(|proc| procedure_module_name(&proc.name)),
)
}
Expand All @@ -954,8 +955,8 @@ fn print_module_reexports(module: &ModuleDef, out: &mut Indenter) {
let type_name = collect_case(Case::Pascal, ty.name.name_segments());
writeln!(out, "pub use {mod_name}::{type_name};")
}
for table in iter_tables(module) {
let mod_name = table_module_name(&table.name);
for (table_name, _) in iter_table_names_and_types(module) {
let mod_name = table_module_name(table_name);
// TODO: More precise reexport: we want:
// - The trait name.
// - The insert, delete and possibly update callback ids.
Expand Down Expand Up @@ -1113,12 +1114,12 @@ fn print_db_update_defn(module: &ModuleDef, out: &mut Indenter) {
out.delimited_block(
"pub struct DbUpdate {",
|out| {
for table in iter_tables(module) {
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
writeln!(
out,
"{}: __sdk::TableUpdate<{}>,",
table_method_name(&table.name),
type_ref_name(module, table.product_type_ref),
table_method_name(table_name),
type_ref_name(module, product_type_ref),
);
}
},
Expand All @@ -1137,13 +1138,13 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate {
match &table_update.table_name[..] {
",
|out| {
for table in iter_tables(module) {
for (table_name, _) in iter_table_names_and_types(module) {
writeln!(
out,
"{:?} => db_update.{}.append({}::parse_table_update(table_update)?),",
table.name.deref(),
table_method_name(&table.name),
table_module_name(&table.name),
table_name.deref(),
table_method_name(table_name),
table_module_name(table_name),
);
}
},
Expand Down Expand Up @@ -1181,21 +1182,28 @@ impl __sdk::InModule for DbUpdate {{
let mut diff = AppliedDiff::default();
",
|out| {
for table in iter_tables(module) {
let with_updates = table
.primary_key
.map(|col| {
let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake);
format!(".with_updates_by_pk(|row| &row.{pk_field})")
})
.unwrap_or_default();

let field_name = table_method_name(&table.name);
for (table_name, product_type_ref, with_updates) in itertools::chain!(
iter_tables(module).map(|table| {
(
&table.name,
table.product_type_ref,
table
.primary_key
.map(|col| {
let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake);
format!(".with_updates_by_pk(|row| &row.{pk_field})")
})
.unwrap_or_default(),
)
}),
iter_views(module).map(|view| (&view.name, view.product_type_ref, "".into()))
) {
let field_name = table_method_name(table_name);
writeln!(
out,
"diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name}){with_updates};",
type_ref_name(module, table.product_type_ref),
table.name.deref(),
type_ref_name(module, product_type_ref),
table_name.deref(),
);
}
},
Expand All @@ -1215,12 +1223,12 @@ fn print_applied_diff_defn(module: &ModuleDef, out: &mut Indenter) {
out.delimited_block(
"pub struct AppliedDiff<'r> {",
|out| {
for table in iter_tables(module) {
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
writeln!(
out,
"{}: __sdk::TableAppliedDiff<'r, {}>,",
table_method_name(&table.name),
type_ref_name(module, table.product_type_ref),
table_method_name(table_name),
type_ref_name(module, product_type_ref),
);
}
// Also write a `PhantomData` field which uses the lifetime `r`,
Expand Down Expand Up @@ -1248,13 +1256,13 @@ impl __sdk::InModule for AppliedDiff<'_> {{
out.delimited_block(
"fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks<RemoteModule>) {",
|out| {
for table in iter_tables(module) {
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
writeln!(
out,
"callbacks.invoke_table_row_callbacks::<{}>({:?}, &self.{}, event);",
type_ref_name(module, table.product_type_ref),
table.name.deref(),
table_method_name(&table.name),
type_ref_name(module, product_type_ref),
table_name.deref(),
table_method_name(table_name),
);
}
},
Expand Down Expand Up @@ -1290,8 +1298,8 @@ type SubscriptionHandle = SubscriptionHandle;
out.delimited_block(
"fn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {",
|out| {
for table in iter_tables(module) {
writeln!(out, "{}::register_table(client_cache);", table_module_name(&table.name));
for (table_name, _) in iter_table_names_and_types(module) {
writeln!(out, "{}::register_table(client_cache);", table_module_name(table_name));
}
},
"}\n",
Expand Down
45 changes: 30 additions & 15 deletions crates/codegen/src/typescript.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::util::{
is_reducer_invokable, iter_reducers, iter_tables, iter_types, iter_unique_cols,
print_auto_generated_version_comment,
is_reducer_invokable, iter_reducers, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols,
iter_views, print_auto_generated_version_comment,
};
use crate::{indent_scope, OutputFile};

Expand Down Expand Up @@ -341,10 +341,9 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type})

writeln!(out);
writeln!(out, "// Import and reexport all table handle types");
for table in iter_tables(module) {
let table_name = &table.name;
for (table_name, _) in iter_table_names_and_types(module) {
let table_module_name = table_module_name(table_name) + ".ts";
let table_name_pascalcase = table.name.deref().to_case(Case::Pascal);
let table_name_pascalcase = table_name.deref().to_case(Case::Pascal);
let table_handle = table_name_pascalcase.clone() + "TableHandle";
writeln!(out, "import {{ {table_handle} }} from \"./{table_module_name}\";");
writeln!(out, "export {{ {table_handle} }};");
Expand All @@ -366,15 +365,31 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type})
out.indent(1);
writeln!(out, "tables: {{");
out.indent(1);
for table in iter_tables(module) {
let type_ref = table.product_type_ref;
for (table_name, product_type_ref, schema) in itertools::chain!(
iter_tables(module).map(|def| {
(
&def.name,
def.product_type_ref,
TableSchema::from_module_def(module, def, (), 0.into()),
)
}),
iter_views(module).map(|def| {
(
&def.name,
def.product_type_ref,
TableSchema::from_view_def_for_codegen(module, def),
)
})
) {
let table_name = table_name.deref();
let type_ref = product_type_ref;
let row_type = type_ref_name(module, type_ref);
let schema = TableSchema::from_module_def(module, table, (), 0.into())
let schema = schema
.validated()
.expect("Failed to generate table due to validation errors");
writeln!(out, "{}: {{", table.name);
writeln!(out, "{}: {{", table_name);
out.indent(1);
writeln!(out, "tableName: \"{}\" as const,", table.name);
writeln!(out, "tableName: \"{}\" as const,", table_name);
writeln!(out, "rowType: {row_type}.getTypeScriptAlgebraicType(),");
if let Some(pk) = schema.pk() {
// This is left here so we can release the codegen change before releasing a new
Expand Down Expand Up @@ -612,13 +627,13 @@ fn print_remote_tables(module: &ModuleDef, out: &mut Indenter) {
out.indent(1);
writeln!(out, "constructor(private connection: __DbConnectionImpl) {{}}");

for table in iter_tables(module) {
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
writeln!(out);
let table_name = table.name.deref();
let table_name_pascalcase = table.name.deref().to_case(Case::Pascal);
let table_name_camelcase = table.name.deref().to_case(Case::Camel);
let table_name = table_name.deref();
let table_name_pascalcase = table_name.to_case(Case::Pascal);
let table_name_camelcase = table_name.to_case(Case::Camel);
let table_handle = table_name_pascalcase.clone() + "TableHandle";
let type_ref = table.product_type_ref;
let type_ref = product_type_ref;
let row_type = type_ref_name(module, type_ref);
writeln!(out, "get {table_name_camelcase}(): {table_handle}<'{table_name}'> {{");
out.with_indent(|out| {
Expand Down
44 changes: 21 additions & 23 deletions crates/codegen/src/unrealcpp.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Autogenerated Unreal‑C++ code‑gen backend for SpacetimeDB CLI
use crate::code_indenter::CodeIndenter;
use crate::util::{
collect_case, fmt_fn, iter_tables, print_auto_generated_file_comment, print_auto_generated_version_comment,
collect_case, fmt_fn, iter_table_names_and_types, print_auto_generated_file_comment,
print_auto_generated_version_comment,
};
use crate::util::{iter_indexes, iter_reducers};
use crate::Lang;
Expand Down Expand Up @@ -683,8 +684,8 @@ impl Lang for UnrealCpp<'_> {
writeln!(client_h);

writeln!(client_h, "/** Forward declaration for tables */");
for table in iter_tables(module) {
let table_pascal = type_ref_name(module, table.product_type_ref);
for (_, product_type_ref) in iter_table_names_and_types(module) {
let table_pascal = type_ref_name(module, product_type_ref);
writeln!(client_h, "class U{table_pascal}Table;");
}
writeln!(client_h, "/***/");
Expand Down Expand Up @@ -774,12 +775,11 @@ impl Lang for UnrealCpp<'_> {
});

// Build table includes
let table_includes: Vec<String> = module
.tables()
.map(|table| {
let table_includes: Vec<String> = iter_table_names_and_types(module)
.map(|(_, product_type_ref)| {
format!(
"ModuleBindings/Tables/{}Table.g.h",
type_ref_name(module, table.product_type_ref)
type_ref_name(module, product_type_ref)
)
})
.collect();
Expand Down Expand Up @@ -1856,14 +1856,14 @@ fn generate_remote_tables_class(output: &mut UnrealCppAutogen, module: &ModuleDe
writeln!(output);

// Generate table handle properties
for table in module.tables() {
let table_pascal = type_ref_name(module, table.product_type_ref);
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
let table_pascal = type_ref_name(module, product_type_ref);

writeln!(output, " UPROPERTY(BlueprintReadOnly, Category=\"SpacetimeDB\")");
writeln!(
output,
" U{table_pascal}Table* {};",
table.name.deref().to_case(Case::Pascal)
table_name.deref().to_case(Case::Pascal)
);
writeln!(output);
}
Expand Down Expand Up @@ -2357,16 +2357,16 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module
writeln!(output, "\tReducers->SetCallReducerFlags = SetReducerFlags;");
writeln!(output, "\tReducers->Conn = this;");
writeln!(output);
for table in module.tables() {
let table_pascal = type_ref_name(module, table.product_type_ref);
let table_name = table.name.deref();
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
let table_pascal = type_ref_name(module, product_type_ref);
let table_name = table_name.deref();
writeln!(
output,
"\tRegisterTable<F{}Type, U{}Table, FEventContext>(TEXT(\"{}\"), Db->{});",
table_pascal,
table_pascal,
table_name,
table.name.deref().to_case(Case::Pascal)
table_name.to_case(Case::Pascal)
);
}
writeln!(output, "}}");
Expand Down Expand Up @@ -2412,23 +2412,23 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module
writeln!(output, "{{");
writeln!(output);
writeln!(output, "\t/** Creating tables */");
for table in module.tables() {
let table_pascal = type_ref_name(module, table.product_type_ref);
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
let table_pascal = type_ref_name(module, product_type_ref);
writeln!(
output,
"\t{} = NewObject<U{}Table>(this);",
table.name.deref().to_case(Case::Pascal),
table_name.deref().to_case(Case::Pascal),
table_pascal
);
}
writeln!(output, "\t/**/");
writeln!(output);
writeln!(output, "\t/** Initialization */");
for table in module.tables() {
for (table_name, _) in iter_table_names_and_types(module) {
writeln!(
output,
"\t{}->PostInitialize();",
table.name.deref().to_case(Case::Pascal)
table_name.deref().to_case(Case::Pascal)
);
}
writeln!(output, "\t/**/");
Expand Down Expand Up @@ -3095,10 +3095,8 @@ fn collect_optional_types(module: &ModuleDef) -> HashSet<String> {
}

// Collect from all tables
for table in module.tables() {
let product_type = module.typespace_for_generate()[table.product_type_ref]
.as_product()
.unwrap();
for (_, product_type_ref) in iter_table_names_and_types(module) {
let product_type = module.typespace_for_generate()[product_type_ref].as_product().unwrap();
for (_, field_ty) in &product_type.elements {
collect_from_type(module, field_ty, &mut optional_types);
}
Expand Down
Loading
Loading