Skip to content

Commit 353cc0d

Browse files
joshua-spacetimejdetterJasonAtClockwork
authored
Client codegen fixes for views (#3690)
# Description of Changes Fixes client codegen for views. # API and ABI breaking changes None # Expected complexity level and risk 2 # Testing I'm not sure what tests we have for C++. - [x] Updated client snapshots for rust, C#, and typescript - [ ] Rust sdk test --------- Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com> Co-authored-by: Jason Larabie <jason@clockworklabs.io>
1 parent 9e3ffeb commit 353cc0d

File tree

15 files changed

+551
-519
lines changed

15 files changed

+551
-519
lines changed

crates/codegen/src/csharp.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use std::ops::Deref;
77
use super::code_indenter::CodeIndenter;
88
use super::Lang;
99
use crate::util::{
10-
collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, print_auto_generated_file_comment,
11-
print_auto_generated_version_comment, type_ref_name,
10+
collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_table_names_and_types,
11+
print_auto_generated_file_comment, print_auto_generated_version_comment, type_ref_name,
1212
};
1313
use crate::{indent_scope, OutputFile};
1414
use convert_case::{Case, Casing};
@@ -745,11 +745,11 @@ impl Lang for Csharp<'_> {
745745
indented_block(&mut output, |output| {
746746
writeln!(output, "public RemoteTables(DbConnection conn)");
747747
indented_block(output, |output| {
748-
for table in iter_tables(module) {
748+
for (table_name, _) in iter_table_names_and_types(module) {
749749
writeln!(
750750
output,
751751
"AddTable({} = new(conn));",
752-
table.name.deref().to_case(Case::Pascal)
752+
table_name.deref().to_case(Case::Pascal)
753753
);
754754
}
755755
});

crates/codegen/src/rust.rs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use super::code_indenter::{CodeIndenter, Indenter};
22
use super::util::{collect_case, iter_reducers, print_lines, type_ref_name};
33
use super::Lang;
44
use crate::util::{
5-
iter_procedures, iter_tables, iter_types, iter_unique_cols, print_auto_generated_file_comment,
6-
print_auto_generated_version_comment,
5+
iter_procedures, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols, iter_views,
6+
print_auto_generated_file_comment, print_auto_generated_version_comment,
77
};
88
use crate::OutputFile;
99
use convert_case::{Case, Casing};
@@ -930,12 +930,13 @@ fn reducer_flags_trait_name(reducer: &ReducerDef) -> String {
930930
format!("set_flags_for_{}", reducer_function_name(reducer))
931931
}
932932

933-
/// Iterate over all of the Rust `mod`s for types, reducers and tables in the `module`.
933+
/// Iterate over all of the Rust `mod`s for types, reducers, views, and tables in the `module`.
934934
fn iter_module_names(module: &ModuleDef) -> impl Iterator<Item = String> + '_ {
935935
itertools::chain!(
936936
iter_types(module).map(|ty| type_module_name(&ty.name)),
937937
iter_reducers(module).map(|r| reducer_module_name(&r.name)),
938938
iter_tables(module).map(|tbl| table_module_name(&tbl.name)),
939+
iter_views(module).map(|view| table_module_name(&view.name)),
939940
iter_procedures(module).map(|proc| procedure_module_name(&proc.name)),
940941
)
941942
}
@@ -954,8 +955,8 @@ fn print_module_reexports(module: &ModuleDef, out: &mut Indenter) {
954955
let type_name = collect_case(Case::Pascal, ty.name.name_segments());
955956
writeln!(out, "pub use {mod_name}::{type_name};")
956957
}
957-
for table in iter_tables(module) {
958-
let mod_name = table_module_name(&table.name);
958+
for (table_name, _) in iter_table_names_and_types(module) {
959+
let mod_name = table_module_name(table_name);
959960
// TODO: More precise reexport: we want:
960961
// - The trait name.
961962
// - The insert, delete and possibly update callback ids.
@@ -1113,12 +1114,12 @@ fn print_db_update_defn(module: &ModuleDef, out: &mut Indenter) {
11131114
out.delimited_block(
11141115
"pub struct DbUpdate {",
11151116
|out| {
1116-
for table in iter_tables(module) {
1117+
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
11171118
writeln!(
11181119
out,
11191120
"{}: __sdk::TableUpdate<{}>,",
1120-
table_method_name(&table.name),
1121-
type_ref_name(module, table.product_type_ref),
1121+
table_method_name(table_name),
1122+
type_ref_name(module, product_type_ref),
11221123
);
11231124
}
11241125
},
@@ -1137,13 +1138,13 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate {
11371138
match &table_update.table_name[..] {
11381139
",
11391140
|out| {
1140-
for table in iter_tables(module) {
1141+
for (table_name, _) in iter_table_names_and_types(module) {
11411142
writeln!(
11421143
out,
11431144
"{:?} => db_update.{}.append({}::parse_table_update(table_update)?),",
1144-
table.name.deref(),
1145-
table_method_name(&table.name),
1146-
table_module_name(&table.name),
1145+
table_name.deref(),
1146+
table_method_name(table_name),
1147+
table_module_name(table_name),
11471148
);
11481149
}
11491150
},
@@ -1198,6 +1199,15 @@ impl __sdk::InModule for DbUpdate {{
11981199
table.name.deref(),
11991200
);
12001201
}
1202+
for view in iter_views(module) {
1203+
let field_name = table_method_name(&view.name);
1204+
writeln!(
1205+
out,
1206+
"diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name});",
1207+
type_ref_name(module, view.product_type_ref),
1208+
view.name.deref(),
1209+
);
1210+
}
12011211
},
12021212
"
12031213
diff
@@ -1215,12 +1225,12 @@ fn print_applied_diff_defn(module: &ModuleDef, out: &mut Indenter) {
12151225
out.delimited_block(
12161226
"pub struct AppliedDiff<'r> {",
12171227
|out| {
1218-
for table in iter_tables(module) {
1228+
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
12191229
writeln!(
12201230
out,
12211231
"{}: __sdk::TableAppliedDiff<'r, {}>,",
1222-
table_method_name(&table.name),
1223-
type_ref_name(module, table.product_type_ref),
1232+
table_method_name(table_name),
1233+
type_ref_name(module, product_type_ref),
12241234
);
12251235
}
12261236
// Also write a `PhantomData` field which uses the lifetime `r`,
@@ -1248,13 +1258,13 @@ impl __sdk::InModule for AppliedDiff<'_> {{
12481258
out.delimited_block(
12491259
"fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks<RemoteModule>) {",
12501260
|out| {
1251-
for table in iter_tables(module) {
1261+
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
12521262
writeln!(
12531263
out,
12541264
"callbacks.invoke_table_row_callbacks::<{}>({:?}, &self.{}, event);",
1255-
type_ref_name(module, table.product_type_ref),
1256-
table.name.deref(),
1257-
table_method_name(&table.name),
1265+
type_ref_name(module, product_type_ref),
1266+
table_name.deref(),
1267+
table_method_name(table_name),
12581268
);
12591269
}
12601270
},
@@ -1290,8 +1300,8 @@ type SubscriptionHandle = SubscriptionHandle;
12901300
out.delimited_block(
12911301
"fn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {",
12921302
|out| {
1293-
for table in iter_tables(module) {
1294-
writeln!(out, "{}::register_table(client_cache);", table_module_name(&table.name));
1303+
for (table_name, _) in iter_table_names_and_types(module) {
1304+
writeln!(out, "{}::register_table(client_cache);", table_module_name(table_name));
12951305
}
12961306
},
12971307
"}\n",

crates/codegen/src/typescript.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::util::{
2-
is_reducer_invokable, iter_constraints, iter_indexes, iter_reducers, iter_table_and_view_names, iter_tables,
2+
is_reducer_invokable, iter_constraints, iter_indexes, iter_reducers, iter_table_names_and_types, iter_tables,
33
iter_types, iter_views, print_auto_generated_version_comment,
44
};
55
use crate::OutputFile;
@@ -195,7 +195,7 @@ impl Lang for TypeScript {
195195

196196
writeln!(out);
197197
writeln!(out, "// Import and reexport all table handle types");
198-
for table_name in iter_table_and_view_names(module) {
198+
for (table_name, _) in iter_table_names_and_types(module) {
199199
let table_module_name = table_module_name(table_name);
200200
let table_name_pascalcase = table_name.deref().to_case(Case::Pascal);
201201
// TODO: This really shouldn't be necessary. We could also have `table()` accept

crates/codegen/src/unrealcpp.rs

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Autogenerated Unreal‑C++ code‑gen backend for SpacetimeDB CLI
22
use crate::code_indenter::CodeIndenter;
33
use crate::util::{
4-
collect_case, fmt_fn, iter_tables, print_auto_generated_file_comment, print_auto_generated_version_comment,
4+
collect_case, fmt_fn, iter_table_names_and_types, print_auto_generated_file_comment,
5+
print_auto_generated_version_comment,
56
};
67
use crate::util::{iter_indexes, iter_reducers};
78
use crate::Lang;
@@ -38,7 +39,8 @@ impl UnrealCpp<'_> {
3839
impl Lang for UnrealCpp<'_> {
3940
fn generate_table_file_from_schema(&self, module: &ModuleDef, table: &TableDef, schema: TableSchema) -> OutputFile {
4041
let struct_name = type_ref_name(module, table.product_type_ref);
41-
let self_header = struct_name.clone() + "Table";
42+
let table_pascal = table.name.deref().to_case(Case::Pascal);
43+
let self_header = table_pascal.clone() + "Table";
4244

4345
let mut output = UnrealCppAutogen::new(
4446
&[
@@ -54,9 +56,8 @@ impl Lang for UnrealCpp<'_> {
5456
);
5557

5658
let row_struct = format!("F{struct_name}Type"); // e.g. "FUserType", "FMessageType"
57-
let handle_cls = format!("U{struct_name}Table"); // "UMessageTable"
59+
let handle_cls = format!("U{table_pascal}Table"); // "UMessageTable"
5860
let table_name = table.name.deref().to_string();
59-
let table_pascal = struct_name.clone();
6061

6162
// Generate unique index classes first
6263
let product_type = module.typespace_for_generate()[table.product_type_ref].as_product();
@@ -374,7 +375,7 @@ impl Lang for UnrealCpp<'_> {
374375
filename: format!(
375376
"Source/{}/Public/ModuleBindings/Tables/{}Table.g.h",
376377
self.module_name,
377-
type_ref_name(module, table.product_type_ref)
378+
table.name.deref().to_case(Case::Pascal) //type_ref_name(module, table.product_type_ref)
378379
),
379380
code: output.into_inner(),
380381
}
@@ -683,9 +684,8 @@ impl Lang for UnrealCpp<'_> {
683684
writeln!(client_h);
684685

685686
writeln!(client_h, "/** Forward declaration for tables */");
686-
for table in iter_tables(module) {
687-
let table_pascal = type_ref_name(module, table.product_type_ref);
688-
writeln!(client_h, "class U{table_pascal}Table;");
687+
for (table_name, _) in iter_table_names_and_types(module) {
688+
writeln!(client_h, "class U{}Table;", table_name.deref().to_case(Case::Pascal));
689689
}
690690
writeln!(client_h, "/***/");
691691
writeln!(client_h);
@@ -774,12 +774,11 @@ impl Lang for UnrealCpp<'_> {
774774
});
775775

776776
// Build table includes
777-
let table_includes: Vec<String> = module
778-
.tables()
779-
.map(|table| {
777+
let table_includes: Vec<String> = iter_table_names_and_types(module)
778+
.map(|(table_name, _)| {
780779
format!(
781780
"ModuleBindings/Tables/{}Table.g.h",
782-
type_ref_name(module, table.product_type_ref)
781+
table_name.deref().to_case(Case::Pascal) //type_ref_name(module, product_type_ref)
783782
)
784783
})
785784
.collect();
@@ -805,26 +804,46 @@ impl Lang for UnrealCpp<'_> {
805804

806805
// Generate .cpp implementation files for each table
807806
for table in module.tables() {
808-
let table_cpp_content = generate_table_cpp(module, table, self.module_name);
807+
let schema = TableSchema::from_module_def(module, table, (), 0.into())
808+
.validated()
809+
.expect("table schema should validate");
810+
let table_cpp_content = generate_table_cpp(module, table, self.module_name, &schema);
809811
let table_cpp_filename = format!(
810812
"Source/{}/Private/ModuleBindings/Tables/{}Table.g.cpp",
811813
self.module_name,
812-
type_ref_name(module, table.product_type_ref)
814+
table.name.deref().to_case(Case::Pascal) //type_ref_name(module, table.product_type_ref)
813815
);
814816
files.push(OutputFile {
815817
filename: table_cpp_filename,
816818
code: table_cpp_content,
817819
});
818820
}
821+
for view in module.views() {
822+
let tbl = TableDef::from(view.clone());
823+
let schema = TableSchema::from_view_def_for_codegen(module, view)
824+
.validated()
825+
.expect("Failed to generate table due to validation errors");
826+
let view_cpp_content = generate_table_cpp(module, &tbl, self.module_name, &schema);
827+
let view_cpp_filename = format!(
828+
"Source/{}/Private/ModuleBindings/Tables/{}Table.g.cpp",
829+
self.module_name,
830+
view.name.deref().to_case(Case::Pascal) //type_ref_name(module, view.product_type_ref)
831+
);
832+
files.push(OutputFile {
833+
filename: view_cpp_filename,
834+
code: view_cpp_content,
835+
});
836+
}
819837

820838
files
821839
}
822840
}
823841

824842
// Helper function to generate table .cpp implementation files
825-
fn generate_table_cpp(module: &ModuleDef, table: &TableDef, module_name: &str) -> String {
826-
let table_pascal = type_ref_name(module, table.product_type_ref);
827-
let row_struct = format!("F{table_pascal}Type");
843+
fn generate_table_cpp(module: &ModuleDef, table: &TableDef, module_name: &str, schema: &TableSchema) -> String {
844+
let table_pascal = table.name.deref().to_case(Case::Pascal);
845+
let struct_name = type_ref_name(module, table.product_type_ref);
846+
let row_struct = format!("F{struct_name}Type");
828847

829848
// Include the table header and other necessary headers
830849
let table_header = format!("ModuleBindings/Tables/{table_pascal}Table.g.h");
@@ -838,10 +857,6 @@ fn generate_table_cpp(module: &ModuleDef, table: &TableDef, module_name: &str) -
838857

839858
let mut output = UnrealCppAutogen::new_cpp(&includes);
840859

841-
let schema = TableSchema::from_module_def(module, table, (), 0.into())
842-
.validated()
843-
.expect("table schema should validate");
844-
845860
// Get unique indexes and non-unique BTree indexes
846861
let product_type = module.typespace_for_generate()[table.product_type_ref].as_product();
847862

@@ -1856,14 +1871,13 @@ fn generate_remote_tables_class(output: &mut UnrealCppAutogen, module: &ModuleDe
18561871
writeln!(output);
18571872

18581873
// Generate table handle properties
1859-
for table in module.tables() {
1860-
let table_pascal = type_ref_name(module, table.product_type_ref);
1861-
1874+
for (table_name, _) in iter_table_names_and_types(module) {
18621875
writeln!(output, " UPROPERTY(BlueprintReadOnly, Category=\"SpacetimeDB\")");
18631876
writeln!(
18641877
output,
1865-
" U{table_pascal}Table* {};",
1866-
table.name.deref().to_case(Case::Pascal)
1878+
" U{}Table* {};",
1879+
table_name.deref().to_case(Case::Pascal),
1880+
table_name.deref().to_case(Case::Pascal)
18671881
);
18681882
writeln!(output);
18691883
}
@@ -2357,16 +2371,16 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module
23572371
writeln!(output, "\tReducers->SetCallReducerFlags = SetReducerFlags;");
23582372
writeln!(output, "\tReducers->Conn = this;");
23592373
writeln!(output);
2360-
for table in module.tables() {
2361-
let table_pascal = type_ref_name(module, table.product_type_ref);
2362-
let table_name = table.name.deref();
2374+
for (table_name, product_type_ref) in iter_table_names_and_types(module) {
2375+
let struct_name = type_ref_name(module, product_type_ref);
2376+
let table_name = table_name.deref();
23632377
writeln!(
23642378
output,
23652379
"\tRegisterTable<F{}Type, U{}Table, FEventContext>(TEXT(\"{}\"), Db->{});",
2366-
table_pascal,
2367-
table_pascal,
2380+
struct_name,
2381+
table_name.to_case(Case::Pascal),
23682382
table_name,
2369-
table.name.deref().to_case(Case::Pascal)
2383+
table_name.to_case(Case::Pascal)
23702384
);
23712385
}
23722386
writeln!(output, "}}");
@@ -2412,23 +2426,22 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module
24122426
writeln!(output, "{{");
24132427
writeln!(output);
24142428
writeln!(output, "\t/** Creating tables */");
2415-
for table in module.tables() {
2416-
let table_pascal = type_ref_name(module, table.product_type_ref);
2429+
for (table_name, _) in iter_table_names_and_types(module) {
24172430
writeln!(
24182431
output,
24192432
"\t{} = NewObject<U{}Table>(this);",
2420-
table.name.deref().to_case(Case::Pascal),
2421-
table_pascal
2433+
table_name.deref().to_case(Case::Pascal),
2434+
table_name.deref().to_case(Case::Pascal)
24222435
);
24232436
}
24242437
writeln!(output, "\t/**/");
24252438
writeln!(output);
24262439
writeln!(output, "\t/** Initialization */");
2427-
for table in module.tables() {
2440+
for (table_name, _) in iter_table_names_and_types(module) {
24282441
writeln!(
24292442
output,
24302443
"\t{}->PostInitialize();",
2431-
table.name.deref().to_case(Case::Pascal)
2444+
table_name.deref().to_case(Case::Pascal)
24322445
);
24332446
}
24342447
writeln!(output, "\t/**/");
@@ -3095,10 +3108,8 @@ fn collect_optional_types(module: &ModuleDef) -> HashSet<String> {
30953108
}
30963109

30973110
// Collect from all tables
3098-
for table in module.tables() {
3099-
let product_type = module.typespace_for_generate()[table.product_type_ref]
3100-
.as_product()
3101-
.unwrap();
3111+
for (_, product_type_ref) in iter_table_names_and_types(module) {
3112+
let product_type = module.typespace_for_generate()[product_type_ref].as_product().unwrap();
31023113
for (_, field_ty) in &product_type.elements {
31033114
collect_from_type(module, field_ty, &mut optional_types);
31043115
}

0 commit comments

Comments
 (0)