Skip to content

Commit

Permalink
websocket: send/receive reducer & table ids instead of names
Browse files Browse the repository at this point in the history
implement ids-no-names in c# sdk codegen

sdk: use strings instead of indices; less efficient, but smaller diff

sdk: check that expected reducers/tables form subset of actual
  • Loading branch information
Centril committed Nov 18, 2024
1 parent ac1d222 commit 868d765
Show file tree
Hide file tree
Showing 53 changed files with 1,469 additions and 1,286 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 29 additions & 35 deletions crates/cli/src/subcommands/generate/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::fmt::{self, Write};
use std::ops::Deref;

use convert_case::{Case, Casing};
use itertools::Itertools;
use spacetimedb_lib::sats::{AlgebraicType, AlgebraicTypeRef, ArrayType, ProductType, SumType};
use spacetimedb_lib::ReducerDef;
use spacetimedb_primitives::ColList;
Expand Down Expand Up @@ -542,13 +543,17 @@ pub fn autogen_csharp_reducer(ctx: &GenCtx, reducer: &ReducerDef, namespace: &st
pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) -> Vec<(String, String)> {
let mut results = Vec::new();

let tables = items.iter().filter_map(|i| {
if let GenItem::Table(table) = i {
Some(table)
} else {
None
}
});
let tables: Vec<_> = items
.iter()
.filter_map(|i| {
if let GenItem::Table(table) = i {
Some(table)
} else {
None
}
})
.sorted_by_key(|t| &t.schema.table_name)
.collect();

let reducers: Vec<&ReducerDef> = items
.iter()
Expand All @@ -559,6 +564,7 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str)
None
}
})
.sorted_by_key(|i| &i.name)
.collect();
let reducer_names: Vec<String> = reducers
.iter()
Expand All @@ -569,7 +575,7 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str)

writeln!(output, "public sealed class RemoteTables");
indented_block(&mut output, |output| {
for table in tables {
for &table in &tables {
let schema = &table.schema;
let name = &schema.table_name;
let csharp_name = name.as_ref().to_case(Case::Pascal);
Expand Down Expand Up @@ -760,9 +766,7 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str)
for reducer_name in &reducer_names {
writeln!(output, "{reducer_name} {reducer_name},");
}
writeln!(output, "Unit StdbNone,");
writeln!(output, "Unit StdbIdentityConnected,");
writeln!(output, "Unit StdbIdentityDisconnected");
writeln!(output, "Unit StdbNone");
}
writeln!(output, ")>;");

Expand All @@ -782,24 +786,25 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str)
writeln!(output, "Reducers = new(this, this.SetReducerFlags);");
writeln!(output);

for item in items {
if let GenItem::Table(table) = item {
writeln!(
output,
"clientDB.AddTable<{table_type}>(\"{table_name}\", Db.{csharp_table_name});",
table_type = csharp_typename(ctx, table.data),
table_name = table.schema.table_name,
csharp_table_name = table.schema.table_name.as_ref().to_case(Case::Pascal)
);
}
for &table in tables.iter() {
writeln!(
output,
"clientDB.AddTable<{table_type}>(\"{table_name}\", Db.{csharp_table_name});",
table_type = csharp_typename(ctx, table.data),
table_name = table.schema.table_name,
csharp_table_name = table.schema.table_name.as_ref().to_case(Case::Pascal)
);
}
});
writeln!(output);

writeln!(output, "protected override Reducer ToReducer(TransactionUpdate update)");
writeln!(
output,
"protected override Reducer ToReducer(string reducerName, TransactionUpdate update)"
);
indented_block(output, |output| {
writeln!(output, "var encodedArgs = update.ReducerCall.Args;");
writeln!(output, "return update.ReducerCall.ReducerName switch {{");
writeln!(output, "return reducerName switch {{");
{
indent_scope!(output);
for (reducer, reducer_name) in std::iter::zip(&reducers, &reducer_names) {
Expand All @@ -810,15 +815,6 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str)
);
}
writeln!(output, "\"<none>\" => new Reducer.StdbNone(default),");
writeln!(
output,
"\"__identity_connected__\" => new Reducer.StdbIdentityConnected(default),"
);
writeln!(
output,
"\"__identity_disconnected__\" => new Reducer.StdbIdentityDisconnected(default),"
);
writeln!(output, "\"\" => new Reducer.StdbNone(default),"); //Transaction from CLI command
writeln!(
output,
r#"var reducer => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {{reducer}}")"#
Expand Down Expand Up @@ -850,9 +846,7 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str)
"Reducer.{reducer_name}(var args) => Reducers.Invoke{reducer_name}(eventContext, args),"
);
}
writeln!(output, "Reducer.StdbNone or");
writeln!(output, "Reducer.StdbIdentityConnected or");
writeln!(output, "Reducer.StdbIdentityDisconnected => true,");
writeln!(output, "Reducer.StdbNone => true,");
writeln!(
output,
r#"_ => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {{reducer}}")"#
Expand Down
9 changes: 4 additions & 5 deletions crates/cli/src/subcommands/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use spacetimedb_schema::schema::{Schema, TableSchema};
use std::fs;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use util::{iter_reducers, iter_tables};
use wasmtime::{Caller, StoreContextMut};

use crate::util::y_or_n;
Expand Down Expand Up @@ -266,9 +267,7 @@ pub fn generate(module: RawModuleDef, lang: Language, namespace: &str) -> anyhow
let items = itertools::chain!(
types,
tables.into_iter().map(GenItem::Table),
reducers
.filter(|r| !(r.name.starts_with("__") && r.name.ends_with("__")))
.map(GenItem::Reducer),
reducers.map(GenItem::Reducer),
);

let items: Vec<GenItem> = items.collect();
Expand All @@ -284,7 +283,7 @@ pub fn generate(module: RawModuleDef, lang: Language, namespace: &str) -> anyhow

fn generate_lang(module: &ModuleDef, lang: impl Lang, namespace: &str) -> Vec<(String, String)> {
itertools::chain!(
module.tables().map(|tbl| {
iter_tables(module).map(|tbl| {
(
lang.table_filename(module, tbl),
lang.generate_table(module, namespace, tbl),
Expand All @@ -296,7 +295,7 @@ fn generate_lang(module: &ModuleDef, lang: impl Lang, namespace: &str) -> Vec<(S
lang.generate_type(module, namespace, typ),
)
}),
module.reducers().map(|reducer| {
iter_reducers(module).map(|reducer| {
(
lang.reducer_filename(&reducer.name),
lang.generate_reducer(module, namespace, reducer),
Expand Down
143 changes: 69 additions & 74 deletions crates/cli/src/subcommands/generate/rust.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::code_indenter::{CodeIndenter, Indenter};
use super::util::{collect_case, print_lines, type_ref_name};
use super::util::{collect_case, iter_reducers, iter_tables, print_lines, type_ref_name};
use super::Lang;
use crate::generate::util::{namespace_is_empty_or_default, print_auto_generated_file_comment};
use convert_case::{Case, Casing};
Expand Down Expand Up @@ -763,20 +763,6 @@ fn print_module_reexports(module: &ModuleDef, out: &mut Indenter) {
}
}

/// Iterate over all the [`ReducerDef`]s defined by the module, in alphabetical order by name.
///
/// Sorting is necessary to have deterministic reproducable codegen.
fn iter_reducers(module: &ModuleDef) -> impl Iterator<Item = &ReducerDef> {
module.reducers().sorted_by_key(|reducer| &reducer.name)
}

/// Iterate over all the [`TableDef`]s defined by the module, in alphabetical order by name.
///
/// Sorting is necessary to have deterministic reproducable codegen.
fn iter_tables(module: &ModuleDef) -> impl Iterator<Item = &TableDef> {
module.tables().sorted_by_key(|table| &table.name)
}

fn print_reducer_enum_defn(module: &ModuleDef, out: &mut Indenter) {
print_enum_derives(out);
writeln!(
Expand Down Expand Up @@ -851,42 +837,45 @@ impl __sdk::spacetime_module::InModule for Reducer {{
},
"}\n",
);
},
"}\n",
);

out.delimited_block(
"impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer {",
|out| {
writeln!(out, "type Error = __anyhow::Error;");
out.delimited_block(
"fn try_from(value: __ws::ReducerCallInfo<__ws::BsatnFormat>) -> __anyhow::Result<Self> {",
|out| {
out.delimited_block(
"match &value.reducer_name[..] {",
|out| {
for reducer in iter_reducers(module) {
writeln!(
out,
"{:?} => Ok(Reducer::{}(__sdk::spacetime_module::parse_reducer_args({:?}, &value.args)?)),",
reducer.name.deref(),
reducer_variant_name(&reducer.name),
reducer.name.deref(),
);
}
"fn parse_call_info(
reducer_id_to_name: &impl Fn(__ws::ReducerId) -> __anyhow::Result<&'static str>,
raw: __ws::ReducerCallInfo<__ws::BsatnFormat>,
) -> __anyhow::Result<Self> {",
|out| {
out.delimited_block(
"use __sdk::spacetime_module::parse_reducer_args;
let name = reducer_id_to_name(raw.reducer_id)?;
match name {",
|out| {
for reducer in iter_reducers(module) {
writeln!(
out,
"_ => Err(__anyhow::anyhow!(\"Unknown reducer {{:?}}\", value.reducer_name)),",
"{:?} => Ok(Reducer::{}(parse_reducer_args(name, &raw.args)?)),",
reducer.name.deref(),
reducer_variant_name(&reducer.name),
);
},
"}\n",
)
},
}
writeln!(out, "_ => unreachable!(),");
},
"
}\n",
)
},
"}\n",
);
out.delimited_block(
"fn reducer_names() -> &'static [&'static str] {&[",
|out| {
for reducer in iter_reducers(module) {
writeln!(out, "{:?},", &reducer.name);
}
},
"]}\n",
);
},
"}\n",
)
);
}

fn print_db_update_defn(module: &ModuleDef, out: &mut Indenter) {
Expand All @@ -910,37 +899,6 @@ fn print_db_update_defn(module: &ModuleDef, out: &mut Indenter) {

out.newline();

out.delimited_block(
"
impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate {
type Error = __anyhow::Error;
fn try_from(raw: __ws::DatabaseUpdate<__ws::BsatnFormat>) -> Result<Self, Self::Error> {
let mut db_update = DbUpdate::default();
for table_update in raw.tables {
match &table_update.table_name[..] {
",
|out| {
for table in iter_tables(module) {
writeln!(
out,
"{:?} => db_update.{} = {}::parse_table_update(table_update)?,",
table.name.deref(),
table_method_name(&table.name),
table_module_name(&table.name),
);
}
},
"
unknown => __anyhow::bail!(\"Unknown table {unknown:?} in DatabaseUpdate\"),
}
}
Ok(db_update)
}
}",
);

out.newline();

writeln!(
out,
"
Expand Down Expand Up @@ -984,6 +942,43 @@ impl __sdk::spacetime_module::InModule for DbUpdate {{
},
"}\n",
);

out.delimited_block(
"fn parse_update(
table_id_to_name: &impl Fn(__ws::TableId) -> __anyhow::Result<&'static str>,
raw: __ws::DatabaseUpdate<__ws::BsatnFormat>,
) -> __anyhow::Result<Self> {
let mut db_update = DbUpdate::default();
for table_update in raw.tables {
match table_id_to_name(table_update.table_id)? {",
|out| {
for table in iter_tables(module) {
writeln!(
out,
"{:?} => db_update.{} = {}::parse_table_update(table_update)?,",
table.name.deref(),
table_method_name(&table.name),
table_module_name(&table.name),
);
}
},
"
_ => unreachable!(),
}
}
Ok(db_update)
}\n",
);

out.delimited_block(
"fn table_names() -> &'static [&'static str] {&[",
|out| {
for table in iter_tables(module) {
writeln!(out, "{:?},", &table.name);
}
},
"]}\n"
);
},
"}\n",
);
Expand Down
19 changes: 18 additions & 1 deletion crates/cli/src/subcommands/generate/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use std::{
use convert_case::{Case, Casing};
use itertools::Itertools;
use spacetimedb_lib::sats::AlgebraicTypeRef;
use spacetimedb_schema::{def::ModuleDef, identifier::Identifier};
use spacetimedb_schema::{
def::{ModuleDef, ReducerDef, TableDef},
identifier::Identifier,
};

use super::code_indenter::Indenter;

Expand Down Expand Up @@ -57,3 +60,17 @@ pub(super) fn type_ref_name(module: &ModuleDef, typeref: AlgebraicTypeRef) -> St
let (name, _def) = module.type_def_from_ref(typeref).unwrap();
collect_case(Case::Pascal, name.name_segments())
}

/// Iterate over all the [`TableDef`]s defined by the module, in alphabetical order by name.
///
/// Sorting is necessary to have deterministic reproducable codegen.
pub(super) fn iter_tables(module: &ModuleDef) -> impl Iterator<Item = &TableDef> {
module.tables().sorted_by_key(|table| &table.name)
}

/// Iterate over all the [`ReducerDef`]s defined by the module, in alphabetical order by name.
///
/// Sorting is necessary to have deterministic reproducable codegen.
pub(super) fn iter_reducers(module: &ModuleDef) -> impl Iterator<Item = &ReducerDef> {
module.reducers().sorted_by_key(|reducer| &reducer.name)
}
2 changes: 1 addition & 1 deletion crates/cli/src/subcommands/subscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ fn reformat_update<'a>(
let table_schema = schema
.tables
.iter()
.find(|tbl| tbl.name == upd.table_name)
.find(|tbl| *tbl.name == *upd.table_name)
.context("table not found in schema")?;
let table_ty = schema.typespace.resolve(table_schema.product_type_ref);

Expand Down
Loading

0 comments on commit 868d765

Please sign in to comment.