diff --git a/Cargo.lock b/Cargo.lock index f18ad1a7985..f88b2938910 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6638,6 +6638,16 @@ dependencies = [ "spacetimedb 1.9.0", ] +[[package]] +name = "sdk-test-view" +version = "0.1.0" +dependencies = [ + "anyhow", + "log", + "paste", + "spacetimedb 1.9.0", +] + [[package]] name = "sdk-unreal-test-harness" version = "1.9.0" @@ -9585,6 +9595,17 @@ dependencies = [ "filetime", ] +[[package]] +name = "view-client" +version = "1.9.0" +dependencies = [ + "anyhow", + "env_logger 0.10.2", + "spacetimedb-lib 1.9.0", + "spacetimedb-sdk", + "test-counter", +] + [[package]] name = "virtue" version = "0.0.18" diff --git a/Cargo.toml b/Cargo.toml index bcc439cf4e4..b4e57bd79e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,10 +44,12 @@ members = [ "modules/sdk-test", "modules/sdk-test-connect-disconnect", "modules/sdk-test-procedure", + "modules/sdk-test-view", "sdks/rust/tests/test-client", "sdks/rust/tests/test-counter", "sdks/rust/tests/connect_disconnect_client", "sdks/rust/tests/procedure-client", + "sdks/rust/tests/view-client", "tools/upgrade-version", "tools/license-check", "tools/replace-spacetimedb", diff --git a/modules/sdk-test-view/.cargo/config.toml b/modules/sdk-test-view/.cargo/config.toml new file mode 100644 index 00000000000..f4e8c002fc2 --- /dev/null +++ b/modules/sdk-test-view/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/modules/sdk-test-view/Cargo.toml b/modules/sdk-test-view/Cargo.toml new file mode 100644 index 00000000000..3647b98505d --- /dev/null +++ b/modules/sdk-test-view/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "sdk-test-view" +version = "0.1.0" +edition.workspace = true +license-file = "LICENSE" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +log.workspace = true +anyhow.workspace = true +paste.workspace = true + +[dependencies.spacetimedb] +workspace = true diff --git a/modules/sdk-test-view/LICENSE b/modules/sdk-test-view/LICENSE new file mode 120000 index 00000000000..8540cf8a991 --- /dev/null +++ b/modules/sdk-test-view/LICENSE @@ -0,0 +1 @@ +../../licenses/BSL.txt \ No newline at end of file diff --git a/modules/sdk-test-view/README.md b/modules/sdk-test-view/README.md new file mode 100644 index 00000000000..10a4b37c05e --- /dev/null +++ b/modules/sdk-test-view/README.md @@ -0,0 +1,14 @@ +# `sdk-test-view` *Rust* test + +This module tests that: +1. Rust client bindings are generated for views +2. You can register callbacks for views just like regular tables +3. Those callbacks are triggered when a view's dependencies are updated + +## How to Run + +Run tests named with `view` in the [Rust client SDK test suite](../../sdks/rust/tests/test.rs): + +```sh +cargo test -p spacetimedb-sdk view +``` diff --git a/modules/sdk-test-view/src/lib.rs b/modules/sdk-test-view/src/lib.rs new file mode 100644 index 00000000000..91a76a12b76 --- /dev/null +++ b/modules/sdk-test-view/src/lib.rs @@ -0,0 +1,75 @@ +use spacetimedb::{ + reducer, table, view, AnonymousViewContext, Identity, ReducerContext, SpacetimeType, Table, ViewContext, +}; + +#[table(name = player)] +struct Player { + #[primary_key] + #[auto_inc] + entity_id: u64, + #[unique] + identity: Identity, +} + +#[table(name = player_level)] +struct PlayerLevel { + #[unique] + entity_id: u64, + #[index(btree)] + level: u64, +} + +#[derive(SpacetimeType)] +struct PlayerAndLevel { + entity_id: u64, + identity: Identity, + level: u64, +} + +#[reducer] +fn insert_player(ctx: &ReducerContext, identity: Identity, level: u64) { + let Player { entity_id, .. } = ctx.db.player().insert(Player { entity_id: 0, identity }); + ctx.db.player_level().insert(PlayerLevel { entity_id, level }); +} + +#[reducer] +fn delete_player(ctx: &ReducerContext, identity: Identity) { + if let Some(player) = ctx.db.player().identity().find(identity) { + ctx.db.player().entity_id().delete(player.entity_id); + ctx.db.player_level().entity_id().delete(player.entity_id); + } +} + +#[view(name = my_player, public)] +fn my_player(ctx: &ViewContext) -> Option { + ctx.db.player().identity().find(ctx.sender) +} + +#[view(name = my_player_and_level, public)] +fn my_player_and_level(ctx: &ViewContext) -> Option { + ctx.db + .player() + .identity() + .find(ctx.sender) + .and_then(|Player { entity_id, identity }| { + ctx.db + .player_level() + .entity_id() + .find(entity_id) + .map(|PlayerLevel { level, .. }| PlayerAndLevel { + entity_id, + identity, + level, + }) + }) +} + +#[view(name = players_at_level_0, public)] +fn players_at_level_0(ctx: &AnonymousViewContext) -> Vec { + ctx.db + .player_level() + .level() + .filter(0u64) + .filter_map(|p| ctx.db.player().entity_id().find(p.entity_id)) + .collect() +} diff --git a/sdks/rust/tests/test.rs b/sdks/rust/tests/test.rs index 5e532e00522..3032337f495 100644 --- a/sdks/rust/tests/test.rs +++ b/sdks/rust/tests/test.rs @@ -329,3 +329,37 @@ macro_rules! procedure_tests { procedure_tests!(rust_procedures, ""); procedure_tests!(typescript_procedures, "-ts"); + +mod view { + use spacetimedb_testing::sdk::Test; + + const MODULE: &str = "sdk-test-view"; + const CLIENT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/view-client"); + + fn make_test(subcommand: &str) -> Test { + Test::builder() + .with_name(subcommand) + .with_module(MODULE) + .with_client(CLIENT) + .with_language("rust") + .with_bindings_dir("src/module_bindings") + .with_compile_command("cargo build") + .with_run_command(format!("cargo run -- {}", subcommand)) + .build() + } + + #[test] + fn subscribe_anonymous_view() { + make_test("view-anonymous-subscribe").run() + } + + #[test] + fn subscribe_non_anonymous_view() { + make_test("view-non-anonymous-subscribe").run() + } + + #[test] + fn subscribe_view_non_table_return() { + make_test("view-non-table-return").run() + } +} diff --git a/sdks/rust/tests/view-client/Cargo.toml b/sdks/rust/tests/view-client/Cargo.toml new file mode 100644 index 00000000000..76c6bb58a8e --- /dev/null +++ b/sdks/rust/tests/view-client/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "view-client" +version.workspace = true +edition.workspace = true +license-file = "LICENSE" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +spacetimedb-sdk = { path = "../.." } +spacetimedb-lib.workspace = true +test-counter = { path = "../test-counter" } +anyhow.workspace = true +env_logger.workspace = true + +[lints] +workspace = true diff --git a/sdks/rust/tests/view-client/LICENSE b/sdks/rust/tests/view-client/LICENSE new file mode 120000 index 00000000000..424c4c33df2 --- /dev/null +++ b/sdks/rust/tests/view-client/LICENSE @@ -0,0 +1 @@ +../../../../licenses/BSL.txt \ No newline at end of file diff --git a/sdks/rust/tests/view-client/README.md b/sdks/rust/tests/view-client/README.md new file mode 100644 index 00000000000..3a6ad752660 --- /dev/null +++ b/sdks/rust/tests/view-client/README.md @@ -0,0 +1,13 @@ +This test client is used with the module: + +- [`sdk-test-view`](/modules/sdk-test-view) + +The goal of the test is to exercise various view related +aspects of the (Rust) module ABI and the rust SDK. + +To (re-)generate the `module_bindings`, from this directory, run: + +```sh +mkdir -p src/module_bindings +spacetime generate --lang rust --out-dir src/module_bindings --project-path ../../../../modules/sdk-test-view +``` diff --git a/sdks/rust/tests/view-client/src/main.rs b/sdks/rust/tests/view-client/src/main.rs new file mode 100644 index 00000000000..71d6f3724d9 --- /dev/null +++ b/sdks/rust/tests/view-client/src/main.rs @@ -0,0 +1,189 @@ +mod module_bindings; + +use module_bindings::*; +use spacetimedb_lib::Identity; +use spacetimedb_sdk::{DbConnectionBuilder, DbContext, Table}; +use test_counter::TestCounter; + +const LOCALHOST: &str = "http://localhost:3000"; + +/// Register a panic hook which will exit the process whenever any thread panics. +/// +/// This allows us to fail tests by panicking in callbacks. +fn exit_on_panic() { + // The default panic hook is responsible for printing the panic message and backtrace to stderr. + // Grab a handle on it, and invoke it in our custom hook before exiting. + let default_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + // Print panic information + default_hook(panic_info); + + // Exit the process with a non-zero code to denote failure. + std::process::exit(1); + })); +} + +fn db_name_or_panic() -> String { + std::env::var("SPACETIME_SDK_TEST_DB_NAME").expect("Failed to read db name from env") +} + +fn main() { + env_logger::init(); + exit_on_panic(); + + let test = std::env::args() + .nth(1) + .expect("Pass a test name as a command-line argument to the test client"); + + match &*test { + "view-anonymous-subscribe" => exec_anonymous_subscribe(), + "view-non-anonymous-subscribe" => exec_non_anonymous_subscribe(), + "view-non-table-return" => exec_non_table_return(), + _ => panic!("Unknown test: {test}"), + } +} + +fn connect_with_then( + test_counter: &std::sync::Arc, + on_connect_suffix: &str, + with_builder: impl FnOnce(DbConnectionBuilder) -> DbConnectionBuilder, + callback: impl FnOnce(&DbConnection) + Send + 'static, +) -> DbConnection { + let connected_result = test_counter.add_test(format!("on_connect_{on_connect_suffix}")); + let name = db_name_or_panic(); + let builder = DbConnection::builder() + .with_module_name(name) + .with_uri(LOCALHOST) + .on_connect(|ctx, _, _| { + callback(ctx); + connected_result(Ok(())); + }) + .on_connect_error(|_ctx, error| panic!("Connect errored: {error:?}")); + let conn = with_builder(builder).build().unwrap(); + conn.run_threaded(); + conn +} + +fn connect_then( + test_counter: &std::sync::Arc, + callback: impl FnOnce(&DbConnection) + Send + 'static, +) -> DbConnection { + connect_with_then(test_counter, "", |x| x, callback) +} + +fn subscribe_these_then( + ctx: &impl RemoteDbContext, + queries: &[&str], + callback: impl FnOnce(&SubscriptionEventContext) + Send + 'static, +) { + ctx.subscription_builder() + .on_applied(callback) + .on_error(|_ctx, error| panic!("Subscription errored: {error:?}")) + .subscribe(queries); +} + +type ResultRecorder = Box)>; + +fn put_result(result: &mut Option, res: Result<(), anyhow::Error>) { + (result.take().unwrap())(res); +} + +fn exec_anonymous_subscribe() { + let test_counter = TestCounter::new(); + let mut insert_0 = Some(test_counter.add_test("insert_0")); + let mut insert_1 = Some(test_counter.add_test("insert_1")); + let mut delete_1 = Some(test_counter.add_test("delete_1")); + connect_then(&test_counter, move |ctx| { + subscribe_these_then(ctx, &["SELECT * FROM players_at_level_0"], move |ctx| { + ctx.db.players_at_level_0().on_insert(move |_, player| { + if player.identity == Identity::from_byte_array([2; 32]) { + return put_result(&mut insert_0, Ok(())); + } + if player.identity == Identity::from_byte_array([4; 32]) { + return put_result(&mut insert_1, Ok(())); + } + unreachable!("Unexpected identity on insert: `{}`", player.identity) + }); + ctx.db.players_at_level_0().on_delete(move |_, player| { + if player.identity == Identity::from_byte_array([4; 32]) { + return put_result(&mut delete_1, Ok(())); + } + unreachable!("Unexpected identity on delete: `{}`", player.identity) + }); + ctx.reducers() + .insert_player(Identity::from_byte_array([1; 32]), 1) + .unwrap(); + ctx.reducers() + .insert_player(Identity::from_byte_array([2; 32]), 0) + .unwrap(); + ctx.reducers() + .insert_player(Identity::from_byte_array([3; 32]), 1) + .unwrap(); + ctx.reducers() + .insert_player(Identity::from_byte_array([4; 32]), 0) + .unwrap(); + ctx.reducers() + .delete_player(Identity::from_byte_array([4; 32])) + .unwrap(); + }); + }); + test_counter.wait_for_all(); +} + +fn exec_non_anonymous_subscribe() { + let test_counter = TestCounter::new(); + let mut insert = Some(test_counter.add_test("insert")); + let mut delete = Some(test_counter.add_test("delete")); + connect_then(&test_counter, move |ctx| { + subscribe_these_then(ctx, &["SELECT * FROM my_player"], move |ctx| { + let my_identity = ctx.identity(); + ctx.db.my_player().on_insert(move |_, player| { + assert_eq!(player.identity, my_identity); + put_result(&mut insert, Ok(())); + }); + ctx.db.my_player().on_delete(move |_, player| { + assert_eq!(player.identity, my_identity); + put_result(&mut delete, Ok(())); + }); + ctx.reducers() + .insert_player(Identity::from_byte_array([1; 32]), 0) + .unwrap(); + ctx.reducers().insert_player(my_identity, 0).unwrap(); + ctx.reducers() + .delete_player(Identity::from_byte_array([1; 32])) + .unwrap(); + ctx.reducers().delete_player(my_identity).unwrap(); + }); + }); + test_counter.wait_for_all(); +} + +fn exec_non_table_return() { + let test_counter = TestCounter::new(); + let mut insert = Some(test_counter.add_test("insert")); + let mut delete = Some(test_counter.add_test("delete")); + connect_then(&test_counter, move |ctx| { + subscribe_these_then(ctx, &["SELECT * FROM my_player_and_level"], move |ctx| { + let my_identity = ctx.identity(); + ctx.db.my_player_and_level().on_insert(move |_, player| { + assert_eq!(player.identity, my_identity); + assert_eq!(player.level, 1); + put_result(&mut insert, Ok(())); + }); + ctx.db.my_player_and_level().on_delete(move |_, player| { + assert_eq!(player.identity, my_identity); + assert_eq!(player.level, 1); + put_result(&mut delete, Ok(())); + }); + ctx.reducers() + .insert_player(Identity::from_byte_array([1; 32]), 0) + .unwrap(); + ctx.reducers().insert_player(my_identity, 1).unwrap(); + ctx.reducers() + .delete_player(Identity::from_byte_array([1; 32])) + .unwrap(); + ctx.reducers().delete_player(my_identity).unwrap(); + }); + }); + test_counter.wait_for_all(); +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/delete_player_reducer.rs b/sdks/rust/tests/view-client/src/module_bindings/delete_player_reducer.rs new file mode 100644 index 00000000000..d05e4bde0b8 --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/delete_player_reducer.rs @@ -0,0 +1,103 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct DeletePlayerArgs { + pub identity: __sdk::Identity, +} + +impl From for super::Reducer { + fn from(args: DeletePlayerArgs) -> Self { + Self::DeletePlayer { + identity: args.identity, + } + } +} + +impl __sdk::InModule for DeletePlayerArgs { + type Module = super::RemoteModule; +} + +pub struct DeletePlayerCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `delete_player`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait delete_player { + /// Request that the remote module invoke the reducer `delete_player` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_delete_player`] callbacks. + fn delete_player(&self, identity: __sdk::Identity) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `delete_player`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`DeletePlayerCallbackId`] can be passed to [`Self::remove_on_delete_player`] + /// to cancel the callback. + fn on_delete_player( + &self, + callback: impl FnMut(&super::ReducerEventContext, &__sdk::Identity) + Send + 'static, + ) -> DeletePlayerCallbackId; + /// Cancel a callback previously registered by [`Self::on_delete_player`], + /// causing it not to run in the future. + fn remove_on_delete_player(&self, callback: DeletePlayerCallbackId); +} + +impl delete_player for super::RemoteReducers { + fn delete_player(&self, identity: __sdk::Identity) -> __sdk::Result<()> { + self.imp.call_reducer("delete_player", DeletePlayerArgs { identity }) + } + fn on_delete_player( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &__sdk::Identity) + Send + 'static, + ) -> DeletePlayerCallbackId { + DeletePlayerCallbackId(self.imp.on_reducer( + "delete_player", + Box::new(move |ctx: &super::ReducerEventContext| { + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::DeletePlayer { identity }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, identity) + }), + )) + } + fn remove_on_delete_player(&self, callback: DeletePlayerCallbackId) { + self.imp.remove_on_reducer("delete_player", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `delete_player`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_delete_player { + /// Set the call-reducer flags for the reducer `delete_player` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn delete_player(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_delete_player for super::SetReducerFlags { + fn delete_player(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("delete_player", flags); + } +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/insert_player_reducer.rs b/sdks/rust/tests/view-client/src/module_bindings/insert_player_reducer.rs new file mode 100644 index 00000000000..42c37d4ebbd --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/insert_player_reducer.rs @@ -0,0 +1,106 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct InsertPlayerArgs { + pub identity: __sdk::Identity, + pub level: u64, +} + +impl From for super::Reducer { + fn from(args: InsertPlayerArgs) -> Self { + Self::InsertPlayer { + identity: args.identity, + level: args.level, + } + } +} + +impl __sdk::InModule for InsertPlayerArgs { + type Module = super::RemoteModule; +} + +pub struct InsertPlayerCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `insert_player`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait insert_player { + /// Request that the remote module invoke the reducer `insert_player` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_insert_player`] callbacks. + fn insert_player(&self, identity: __sdk::Identity, level: u64) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `insert_player`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`InsertPlayerCallbackId`] can be passed to [`Self::remove_on_insert_player`] + /// to cancel the callback. + fn on_insert_player( + &self, + callback: impl FnMut(&super::ReducerEventContext, &__sdk::Identity, &u64) + Send + 'static, + ) -> InsertPlayerCallbackId; + /// Cancel a callback previously registered by [`Self::on_insert_player`], + /// causing it not to run in the future. + fn remove_on_insert_player(&self, callback: InsertPlayerCallbackId); +} + +impl insert_player for super::RemoteReducers { + fn insert_player(&self, identity: __sdk::Identity, level: u64) -> __sdk::Result<()> { + self.imp + .call_reducer("insert_player", InsertPlayerArgs { identity, level }) + } + fn on_insert_player( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &__sdk::Identity, &u64) + Send + 'static, + ) -> InsertPlayerCallbackId { + InsertPlayerCallbackId(self.imp.on_reducer( + "insert_player", + Box::new(move |ctx: &super::ReducerEventContext| { + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::InsertPlayer { identity, level }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, identity, level) + }), + )) + } + fn remove_on_insert_player(&self, callback: InsertPlayerCallbackId) { + self.imp.remove_on_reducer("insert_player", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `insert_player`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_insert_player { + /// Set the call-reducer flags for the reducer `insert_player` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn insert_player(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_insert_player for super::SetReducerFlags { + fn insert_player(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("insert_player", flags); + } +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/mod.rs b/sdks/rust/tests/view-client/src/module_bindings/mod.rs new file mode 100644 index 00000000000..012702e25f9 --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/mod.rs @@ -0,0 +1,884 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +// This was generated using spacetimedb cli version 1.9.0 (commit 71b592fb451375323e879b842268764e23417664). + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +pub mod delete_player_reducer; +pub mod insert_player_reducer; +pub mod my_player_and_level_table; +pub mod my_player_table; +pub mod player_and_level_type; +pub mod player_level_table; +pub mod player_level_type; +pub mod player_table; +pub mod player_type; +pub mod players_at_level_0_table; + +pub use delete_player_reducer::{delete_player, set_flags_for_delete_player, DeletePlayerCallbackId}; +pub use insert_player_reducer::{insert_player, set_flags_for_insert_player, InsertPlayerCallbackId}; +pub use my_player_and_level_table::*; +pub use my_player_table::*; +pub use player_and_level_type::PlayerAndLevel; +pub use player_level_table::*; +pub use player_level_type::PlayerLevel; +pub use player_table::*; +pub use player_type::Player; +pub use players_at_level_0_table::*; + +#[derive(Clone, PartialEq, Debug)] + +/// One of the reducers defined by this module. +/// +/// Contained within a [`__sdk::ReducerEvent`] in [`EventContext`]s for reducer events +/// to indicate which reducer caused the event. + +pub enum Reducer { + DeletePlayer { identity: __sdk::Identity }, + InsertPlayer { identity: __sdk::Identity, level: u64 }, +} + +impl __sdk::InModule for Reducer { + type Module = RemoteModule; +} + +impl __sdk::Reducer for Reducer { + fn reducer_name(&self) -> &'static str { + match self { + Reducer::DeletePlayer { .. } => "delete_player", + Reducer::InsertPlayer { .. } => "insert_player", + _ => unreachable!(), + } + } +} +impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { + type Error = __sdk::Error; + fn try_from(value: __ws::ReducerCallInfo<__ws::BsatnFormat>) -> __sdk::Result { + match &value.reducer_name[..] { + "delete_player" => Ok(__sdk::parse_reducer_args::( + "delete_player", + &value.args, + )? + .into()), + "insert_player" => Ok(__sdk::parse_reducer_args::( + "insert_player", + &value.args, + )? + .into()), + unknown => Err(__sdk::InternalError::unknown_name("reducer", unknown, "ReducerCallInfo").into()), + } + } +} + +#[derive(Default)] +#[allow(non_snake_case)] +#[doc(hidden)] +pub struct DbUpdate { + my_player: __sdk::TableUpdate, + my_player_and_level: __sdk::TableUpdate, + player: __sdk::TableUpdate, + player_level: __sdk::TableUpdate, + players_at_level_0: __sdk::TableUpdate, +} + +impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { + type Error = __sdk::Error; + fn try_from(raw: __ws::DatabaseUpdate<__ws::BsatnFormat>) -> Result { + let mut db_update = DbUpdate::default(); + for table_update in raw.tables { + match &table_update.table_name[..] { + "my_player" => db_update + .my_player + .append(my_player_table::parse_table_update(table_update)?), + "my_player_and_level" => db_update + .my_player_and_level + .append(my_player_and_level_table::parse_table_update(table_update)?), + "player" => db_update.player.append(player_table::parse_table_update(table_update)?), + "player_level" => db_update + .player_level + .append(player_level_table::parse_table_update(table_update)?), + "players_at_level_0" => db_update + .players_at_level_0 + .append(players_at_level_0_table::parse_table_update(table_update)?), + + unknown => { + return Err(__sdk::InternalError::unknown_name("table", unknown, "DatabaseUpdate").into()); + } + } + } + Ok(db_update) + } +} + +impl __sdk::InModule for DbUpdate { + type Module = RemoteModule; +} + +impl __sdk::DbUpdate for DbUpdate { + fn apply_to_client_cache(&self, cache: &mut __sdk::ClientCache) -> AppliedDiff<'_> { + let mut diff = AppliedDiff::default(); + + diff.player = cache + .apply_diff_to_table::("player", &self.player) + .with_updates_by_pk(|row| &row.entity_id); + diff.player_level = cache.apply_diff_to_table::("player_level", &self.player_level); + diff.my_player = cache.apply_diff_to_table::("my_player", &self.my_player); + diff.my_player_and_level = + cache.apply_diff_to_table::("my_player_and_level", &self.my_player_and_level); + diff.players_at_level_0 = cache.apply_diff_to_table::("players_at_level_0", &self.players_at_level_0); + + diff + } +} + +#[derive(Default)] +#[allow(non_snake_case)] +#[doc(hidden)] +pub struct AppliedDiff<'r> { + my_player: __sdk::TableAppliedDiff<'r, Player>, + my_player_and_level: __sdk::TableAppliedDiff<'r, PlayerAndLevel>, + player: __sdk::TableAppliedDiff<'r, Player>, + player_level: __sdk::TableAppliedDiff<'r, PlayerLevel>, + players_at_level_0: __sdk::TableAppliedDiff<'r, Player>, + __unused: std::marker::PhantomData<&'r ()>, +} + +impl __sdk::InModule for AppliedDiff<'_> { + type Module = RemoteModule; +} + +impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { + fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks) { + callbacks.invoke_table_row_callbacks::("my_player", &self.my_player, event); + callbacks.invoke_table_row_callbacks::("my_player_and_level", &self.my_player_and_level, event); + callbacks.invoke_table_row_callbacks::("player", &self.player, event); + callbacks.invoke_table_row_callbacks::("player_level", &self.player_level, event); + callbacks.invoke_table_row_callbacks::("players_at_level_0", &self.players_at_level_0, event); + } +} + +#[doc(hidden)] +pub struct RemoteModule; + +impl __sdk::InModule for RemoteModule { + type Module = Self; +} + +/// The `reducers` field of [`EventContext`] and [`DbConnection`], +/// with methods provided by extension traits for each reducer defined by the module. +pub struct RemoteReducers { + imp: __sdk::DbContextImpl, +} + +impl __sdk::InModule for RemoteReducers { + type Module = RemoteModule; +} + +/// The `procedures` field of [`DbConnection`] and other [`DbContext`] types, +/// with methods provided by extension traits for each procedure defined by the module. +pub struct RemoteProcedures { + imp: __sdk::DbContextImpl, +} + +impl __sdk::InModule for RemoteProcedures { + type Module = RemoteModule; +} + +#[doc(hidden)] +/// The `set_reducer_flags` field of [`DbConnection`], +/// with methods provided by extension traits for each reducer defined by the module. +/// Each method sets the flags for the reducer with the same name. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub struct SetReducerFlags { + imp: __sdk::DbContextImpl, +} + +impl __sdk::InModule for SetReducerFlags { + type Module = RemoteModule; +} + +/// The `db` field of [`EventContext`] and [`DbConnection`], +/// with methods provided by extension traits for each table defined by the module. +pub struct RemoteTables { + imp: __sdk::DbContextImpl, +} + +impl __sdk::InModule for RemoteTables { + type Module = RemoteModule; +} + +/// A connection to a remote module, including a materialized view of a subset of the database. +/// +/// Connect to a remote module by calling [`DbConnection::builder`] +/// and using the [`__sdk::DbConnectionBuilder`] builder-pattern constructor. +/// +/// You must explicitly advance the connection by calling any one of: +/// +/// - [`DbConnection::frame_tick`]. +/// - [`DbConnection::run_threaded`]. +/// - [`DbConnection::run_async`]. +/// - [`DbConnection::advance_one_message`]. +/// - [`DbConnection::advance_one_message_blocking`]. +/// - [`DbConnection::advance_one_message_async`]. +/// +/// Which of these methods you should call depends on the specific needs of your application, +/// but you must call one of them, or else the connection will never progress. +pub struct DbConnection { + /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`]. + pub db: RemoteTables, + /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`]. + pub reducers: RemoteReducers, + #[doc(hidden)] + /// Access to setting the call-flags of each reducer defined for each reducer defined by the module + /// via extension traits implemented for [`SetReducerFlags`]. + /// + /// This type is currently unstable and may be removed without a major version bump. + pub set_reducer_flags: SetReducerFlags, + + /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`]. + pub procedures: RemoteProcedures, + + imp: __sdk::DbContextImpl, +} + +impl __sdk::InModule for DbConnection { + type Module = RemoteModule; +} + +impl __sdk::DbContext for DbConnection { + type DbView = RemoteTables; + type Reducers = RemoteReducers; + type Procedures = RemoteProcedures; + type SetReducerFlags = SetReducerFlags; + + fn db(&self) -> &Self::DbView { + &self.db + } + fn reducers(&self) -> &Self::Reducers { + &self.reducers + } + fn procedures(&self) -> &Self::Procedures { + &self.procedures + } + fn set_reducer_flags(&self) -> &Self::SetReducerFlags { + &self.set_reducer_flags + } + + fn is_active(&self) -> bool { + self.imp.is_active() + } + + fn disconnect(&self) -> __sdk::Result<()> { + self.imp.disconnect() + } + + type SubscriptionBuilder = __sdk::SubscriptionBuilder; + + fn subscription_builder(&self) -> Self::SubscriptionBuilder { + __sdk::SubscriptionBuilder::new(&self.imp) + } + + fn try_identity(&self) -> Option<__sdk::Identity> { + self.imp.try_identity() + } + fn connection_id(&self) -> __sdk::ConnectionId { + self.imp.connection_id() + } + fn try_connection_id(&self) -> Option<__sdk::ConnectionId> { + self.imp.try_connection_id() + } +} + +impl DbConnection { + /// Builder-pattern constructor for a connection to a remote module. + /// + /// See [`__sdk::DbConnectionBuilder`] for required and optional configuration for the new connection. + pub fn builder() -> __sdk::DbConnectionBuilder { + __sdk::DbConnectionBuilder::new() + } + + /// If any WebSocket messages are waiting, process one of them. + /// + /// Returns `true` if a message was processed, or `false` if the queue is empty. + /// Callers should invoke this message in a loop until it returns `false` + /// or for as much time is available to process messages. + /// + /// Returns an error if the connection is disconnected. + /// If the disconnection in question was normal, + /// i.e. the result of a call to [`__sdk::DbContext::disconnect`], + /// the returned error will be downcastable to [`__sdk::DisconnectedError`]. + /// + /// This is a low-level primitive exposed for power users who need significant control over scheduling. + /// Most applications should call [`Self::frame_tick`] each frame + /// to fully exhaust the queue whenever time is available. + pub fn advance_one_message(&self) -> __sdk::Result { + self.imp.advance_one_message() + } + + /// Process one WebSocket message, potentially blocking the current thread until one is received. + /// + /// Returns an error if the connection is disconnected. + /// If the disconnection in question was normal, + /// i.e. the result of a call to [`__sdk::DbContext::disconnect`], + /// the returned error will be downcastable to [`__sdk::DisconnectedError`]. + /// + /// This is a low-level primitive exposed for power users who need significant control over scheduling. + /// Most applications should call [`Self::run_threaded`] to spawn a thread + /// which advances the connection automatically. + pub fn advance_one_message_blocking(&self) -> __sdk::Result<()> { + self.imp.advance_one_message_blocking() + } + + /// Process one WebSocket message, `await`ing until one is received. + /// + /// Returns an error if the connection is disconnected. + /// If the disconnection in question was normal, + /// i.e. the result of a call to [`__sdk::DbContext::disconnect`], + /// the returned error will be downcastable to [`__sdk::DisconnectedError`]. + /// + /// This is a low-level primitive exposed for power users who need significant control over scheduling. + /// Most applications should call [`Self::run_async`] to run an `async` loop + /// which advances the connection when polled. + pub async fn advance_one_message_async(&self) -> __sdk::Result<()> { + self.imp.advance_one_message_async().await + } + + /// Process all WebSocket messages waiting in the queue, + /// then return without `await`ing or blocking the current thread. + pub fn frame_tick(&self) -> __sdk::Result<()> { + self.imp.frame_tick() + } + + /// Spawn a thread which processes WebSocket messages as they are received. + pub fn run_threaded(&self) -> std::thread::JoinHandle<()> { + self.imp.run_threaded() + } + + /// Run an `async` loop which processes WebSocket messages when polled. + pub async fn run_async(&self) -> __sdk::Result<()> { + self.imp.run_async().await + } +} + +impl __sdk::DbConnection for DbConnection { + fn new(imp: __sdk::DbContextImpl) -> Self { + Self { + db: RemoteTables { imp: imp.clone() }, + reducers: RemoteReducers { imp: imp.clone() }, + procedures: RemoteProcedures { imp: imp.clone() }, + set_reducer_flags: SetReducerFlags { imp: imp.clone() }, + imp, + } + } +} + +/// A handle on a subscribed query. +// TODO: Document this better after implementing the new subscription API. +#[derive(Clone)] +pub struct SubscriptionHandle { + imp: __sdk::SubscriptionHandleImpl, +} + +impl __sdk::InModule for SubscriptionHandle { + type Module = RemoteModule; +} + +impl __sdk::SubscriptionHandle for SubscriptionHandle { + fn new(imp: __sdk::SubscriptionHandleImpl) -> Self { + Self { imp } + } + + /// Returns true if this subscription has been terminated due to an unsubscribe call or an error. + fn is_ended(&self) -> bool { + self.imp.is_ended() + } + + /// Returns true if this subscription has been applied and has not yet been unsubscribed. + fn is_active(&self) -> bool { + self.imp.is_active() + } + + /// Unsubscribe from the query controlled by this `SubscriptionHandle`, + /// then run `on_end` when its rows are removed from the client cache. + fn unsubscribe_then(self, on_end: __sdk::OnEndedCallback) -> __sdk::Result<()> { + self.imp.unsubscribe_then(Some(on_end)) + } + + fn unsubscribe(self) -> __sdk::Result<()> { + self.imp.unsubscribe_then(None) + } +} + +/// Alias trait for a [`__sdk::DbContext`] connected to this module, +/// with that trait's associated types bounded to this module's concrete types. +/// +/// Users can use this trait as a boundary on definitions which should accept +/// either a [`DbConnection`] or an [`EventContext`] and operate on either. +pub trait RemoteDbContext: + __sdk::DbContext< + DbView = RemoteTables, + Reducers = RemoteReducers, + SetReducerFlags = SetReducerFlags, + SubscriptionBuilder = __sdk::SubscriptionBuilder, +> +{ +} +impl< + Ctx: __sdk::DbContext< + DbView = RemoteTables, + Reducers = RemoteReducers, + SetReducerFlags = SetReducerFlags, + SubscriptionBuilder = __sdk::SubscriptionBuilder, + >, + > RemoteDbContext for Ctx +{ +} + +/// An [`__sdk::DbContext`] augmented with a [`__sdk::Event`], +/// passed to [`__sdk::Table::on_insert`], [`__sdk::Table::on_delete`] and [`__sdk::TableWithPrimaryKey::on_update`] callbacks. +pub struct EventContext { + /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`]. + pub db: RemoteTables, + /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`]. + pub reducers: RemoteReducers, + /// Access to setting the call-flags of each reducer defined for each reducer defined by the module + /// via extension traits implemented for [`SetReducerFlags`]. + /// + /// This type is currently unstable and may be removed without a major version bump. + pub set_reducer_flags: SetReducerFlags, + /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`]. + pub procedures: RemoteProcedures, + /// The event which caused these callbacks to run. + pub event: __sdk::Event, + imp: __sdk::DbContextImpl, +} + +impl __sdk::AbstractEventContext for EventContext { + type Event = __sdk::Event; + fn event(&self) -> &Self::Event { + &self.event + } + fn new(imp: __sdk::DbContextImpl, event: Self::Event) -> Self { + Self { + db: RemoteTables { imp: imp.clone() }, + reducers: RemoteReducers { imp: imp.clone() }, + set_reducer_flags: SetReducerFlags { imp: imp.clone() }, + procedures: RemoteProcedures { imp: imp.clone() }, + event, + imp, + } + } +} + +impl __sdk::InModule for EventContext { + type Module = RemoteModule; +} + +impl __sdk::DbContext for EventContext { + type DbView = RemoteTables; + type Reducers = RemoteReducers; + type Procedures = RemoteProcedures; + type SetReducerFlags = SetReducerFlags; + + fn db(&self) -> &Self::DbView { + &self.db + } + fn reducers(&self) -> &Self::Reducers { + &self.reducers + } + fn procedures(&self) -> &Self::Procedures { + &self.procedures + } + fn set_reducer_flags(&self) -> &Self::SetReducerFlags { + &self.set_reducer_flags + } + + fn is_active(&self) -> bool { + self.imp.is_active() + } + + fn disconnect(&self) -> __sdk::Result<()> { + self.imp.disconnect() + } + + type SubscriptionBuilder = __sdk::SubscriptionBuilder; + + fn subscription_builder(&self) -> Self::SubscriptionBuilder { + __sdk::SubscriptionBuilder::new(&self.imp) + } + + fn try_identity(&self) -> Option<__sdk::Identity> { + self.imp.try_identity() + } + fn connection_id(&self) -> __sdk::ConnectionId { + self.imp.connection_id() + } + fn try_connection_id(&self) -> Option<__sdk::ConnectionId> { + self.imp.try_connection_id() + } +} + +impl __sdk::EventContext for EventContext {} + +/// An [`__sdk::DbContext`] augmented with a [`__sdk::ReducerEvent`], +/// passed to on-reducer callbacks. +pub struct ReducerEventContext { + /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`]. + pub db: RemoteTables, + /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`]. + pub reducers: RemoteReducers, + /// Access to setting the call-flags of each reducer defined for each reducer defined by the module + /// via extension traits implemented for [`SetReducerFlags`]. + /// + /// This type is currently unstable and may be removed without a major version bump. + pub set_reducer_flags: SetReducerFlags, + /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`]. + pub procedures: RemoteProcedures, + /// The event which caused these callbacks to run. + pub event: __sdk::ReducerEvent, + imp: __sdk::DbContextImpl, +} + +impl __sdk::AbstractEventContext for ReducerEventContext { + type Event = __sdk::ReducerEvent; + fn event(&self) -> &Self::Event { + &self.event + } + fn new(imp: __sdk::DbContextImpl, event: Self::Event) -> Self { + Self { + db: RemoteTables { imp: imp.clone() }, + reducers: RemoteReducers { imp: imp.clone() }, + set_reducer_flags: SetReducerFlags { imp: imp.clone() }, + procedures: RemoteProcedures { imp: imp.clone() }, + event, + imp, + } + } +} + +impl __sdk::InModule for ReducerEventContext { + type Module = RemoteModule; +} + +impl __sdk::DbContext for ReducerEventContext { + type DbView = RemoteTables; + type Reducers = RemoteReducers; + type Procedures = RemoteProcedures; + type SetReducerFlags = SetReducerFlags; + + fn db(&self) -> &Self::DbView { + &self.db + } + fn reducers(&self) -> &Self::Reducers { + &self.reducers + } + fn procedures(&self) -> &Self::Procedures { + &self.procedures + } + fn set_reducer_flags(&self) -> &Self::SetReducerFlags { + &self.set_reducer_flags + } + + fn is_active(&self) -> bool { + self.imp.is_active() + } + + fn disconnect(&self) -> __sdk::Result<()> { + self.imp.disconnect() + } + + type SubscriptionBuilder = __sdk::SubscriptionBuilder; + + fn subscription_builder(&self) -> Self::SubscriptionBuilder { + __sdk::SubscriptionBuilder::new(&self.imp) + } + + fn try_identity(&self) -> Option<__sdk::Identity> { + self.imp.try_identity() + } + fn connection_id(&self) -> __sdk::ConnectionId { + self.imp.connection_id() + } + fn try_connection_id(&self) -> Option<__sdk::ConnectionId> { + self.imp.try_connection_id() + } +} + +impl __sdk::ReducerEventContext for ReducerEventContext {} + +/// An [`__sdk::DbContext`] passed to procedure callbacks. +pub struct ProcedureEventContext { + /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`]. + pub db: RemoteTables, + /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`]. + pub reducers: RemoteReducers, + /// Access to setting the call-flags of each reducer defined for each reducer defined by the module + /// via extension traits implemented for [`SetReducerFlags`]. + /// + /// This type is currently unstable and may be removed without a major version bump. + pub set_reducer_flags: SetReducerFlags, + /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`]. + pub procedures: RemoteProcedures, + imp: __sdk::DbContextImpl, +} + +impl __sdk::AbstractEventContext for ProcedureEventContext { + type Event = (); + fn event(&self) -> &Self::Event { + &() + } + fn new(imp: __sdk::DbContextImpl, _event: Self::Event) -> Self { + Self { + db: RemoteTables { imp: imp.clone() }, + reducers: RemoteReducers { imp: imp.clone() }, + procedures: RemoteProcedures { imp: imp.clone() }, + set_reducer_flags: SetReducerFlags { imp: imp.clone() }, + imp, + } + } +} + +impl __sdk::InModule for ProcedureEventContext { + type Module = RemoteModule; +} + +impl __sdk::DbContext for ProcedureEventContext { + type DbView = RemoteTables; + type Reducers = RemoteReducers; + type Procedures = RemoteProcedures; + type SetReducerFlags = SetReducerFlags; + + fn db(&self) -> &Self::DbView { + &self.db + } + fn reducers(&self) -> &Self::Reducers { + &self.reducers + } + fn procedures(&self) -> &Self::Procedures { + &self.procedures + } + fn set_reducer_flags(&self) -> &Self::SetReducerFlags { + &self.set_reducer_flags + } + + fn is_active(&self) -> bool { + self.imp.is_active() + } + + fn disconnect(&self) -> __sdk::Result<()> { + self.imp.disconnect() + } + + type SubscriptionBuilder = __sdk::SubscriptionBuilder; + + fn subscription_builder(&self) -> Self::SubscriptionBuilder { + __sdk::SubscriptionBuilder::new(&self.imp) + } + + fn try_identity(&self) -> Option<__sdk::Identity> { + self.imp.try_identity() + } + fn connection_id(&self) -> __sdk::ConnectionId { + self.imp.connection_id() + } + fn try_connection_id(&self) -> Option<__sdk::ConnectionId> { + self.imp.try_connection_id() + } +} + +impl __sdk::ProcedureEventContext for ProcedureEventContext {} + +/// An [`__sdk::DbContext`] passed to [`__sdk::SubscriptionBuilder::on_applied`] and [`SubscriptionHandle::unsubscribe_then`] callbacks. +pub struct SubscriptionEventContext { + /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`]. + pub db: RemoteTables, + /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`]. + pub reducers: RemoteReducers, + /// Access to setting the call-flags of each reducer defined for each reducer defined by the module + /// via extension traits implemented for [`SetReducerFlags`]. + /// + /// This type is currently unstable and may be removed without a major version bump. + pub set_reducer_flags: SetReducerFlags, + /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`]. + pub procedures: RemoteProcedures, + imp: __sdk::DbContextImpl, +} + +impl __sdk::AbstractEventContext for SubscriptionEventContext { + type Event = (); + fn event(&self) -> &Self::Event { + &() + } + fn new(imp: __sdk::DbContextImpl, _event: Self::Event) -> Self { + Self { + db: RemoteTables { imp: imp.clone() }, + reducers: RemoteReducers { imp: imp.clone() }, + procedures: RemoteProcedures { imp: imp.clone() }, + set_reducer_flags: SetReducerFlags { imp: imp.clone() }, + imp, + } + } +} + +impl __sdk::InModule for SubscriptionEventContext { + type Module = RemoteModule; +} + +impl __sdk::DbContext for SubscriptionEventContext { + type DbView = RemoteTables; + type Reducers = RemoteReducers; + type Procedures = RemoteProcedures; + type SetReducerFlags = SetReducerFlags; + + fn db(&self) -> &Self::DbView { + &self.db + } + fn reducers(&self) -> &Self::Reducers { + &self.reducers + } + fn procedures(&self) -> &Self::Procedures { + &self.procedures + } + fn set_reducer_flags(&self) -> &Self::SetReducerFlags { + &self.set_reducer_flags + } + + fn is_active(&self) -> bool { + self.imp.is_active() + } + + fn disconnect(&self) -> __sdk::Result<()> { + self.imp.disconnect() + } + + type SubscriptionBuilder = __sdk::SubscriptionBuilder; + + fn subscription_builder(&self) -> Self::SubscriptionBuilder { + __sdk::SubscriptionBuilder::new(&self.imp) + } + + fn try_identity(&self) -> Option<__sdk::Identity> { + self.imp.try_identity() + } + fn connection_id(&self) -> __sdk::ConnectionId { + self.imp.connection_id() + } + fn try_connection_id(&self) -> Option<__sdk::ConnectionId> { + self.imp.try_connection_id() + } +} + +impl __sdk::SubscriptionEventContext for SubscriptionEventContext {} + +/// An [`__sdk::DbContext`] augmented with a [`__sdk::Error`], +/// passed to [`__sdk::DbConnectionBuilder::on_disconnect`], [`__sdk::DbConnectionBuilder::on_connect_error`] and [`__sdk::SubscriptionBuilder::on_error`] callbacks. +pub struct ErrorContext { + /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`]. + pub db: RemoteTables, + /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`]. + pub reducers: RemoteReducers, + /// Access to setting the call-flags of each reducer defined for each reducer defined by the module + /// via extension traits implemented for [`SetReducerFlags`]. + /// + /// This type is currently unstable and may be removed without a major version bump. + pub set_reducer_flags: SetReducerFlags, + /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`]. + pub procedures: RemoteProcedures, + /// The event which caused these callbacks to run. + pub event: Option<__sdk::Error>, + imp: __sdk::DbContextImpl, +} + +impl __sdk::AbstractEventContext for ErrorContext { + type Event = Option<__sdk::Error>; + fn event(&self) -> &Self::Event { + &self.event + } + fn new(imp: __sdk::DbContextImpl, event: Self::Event) -> Self { + Self { + db: RemoteTables { imp: imp.clone() }, + reducers: RemoteReducers { imp: imp.clone() }, + set_reducer_flags: SetReducerFlags { imp: imp.clone() }, + procedures: RemoteProcedures { imp: imp.clone() }, + event, + imp, + } + } +} + +impl __sdk::InModule for ErrorContext { + type Module = RemoteModule; +} + +impl __sdk::DbContext for ErrorContext { + type DbView = RemoteTables; + type Reducers = RemoteReducers; + type Procedures = RemoteProcedures; + type SetReducerFlags = SetReducerFlags; + + fn db(&self) -> &Self::DbView { + &self.db + } + fn reducers(&self) -> &Self::Reducers { + &self.reducers + } + fn procedures(&self) -> &Self::Procedures { + &self.procedures + } + fn set_reducer_flags(&self) -> &Self::SetReducerFlags { + &self.set_reducer_flags + } + + fn is_active(&self) -> bool { + self.imp.is_active() + } + + fn disconnect(&self) -> __sdk::Result<()> { + self.imp.disconnect() + } + + type SubscriptionBuilder = __sdk::SubscriptionBuilder; + + fn subscription_builder(&self) -> Self::SubscriptionBuilder { + __sdk::SubscriptionBuilder::new(&self.imp) + } + + fn try_identity(&self) -> Option<__sdk::Identity> { + self.imp.try_identity() + } + fn connection_id(&self) -> __sdk::ConnectionId { + self.imp.connection_id() + } + fn try_connection_id(&self) -> Option<__sdk::ConnectionId> { + self.imp.try_connection_id() + } +} + +impl __sdk::ErrorContext for ErrorContext {} + +impl __sdk::SpacetimeModule for RemoteModule { + type DbConnection = DbConnection; + type EventContext = EventContext; + type ReducerEventContext = ReducerEventContext; + type ProcedureEventContext = ProcedureEventContext; + type SubscriptionEventContext = SubscriptionEventContext; + type ErrorContext = ErrorContext; + type Reducer = Reducer; + type DbView = RemoteTables; + type Reducers = RemoteReducers; + type SetReducerFlags = SetReducerFlags; + type DbUpdate = DbUpdate; + type AppliedDiff<'r> = AppliedDiff<'r>; + type SubscriptionHandle = SubscriptionHandle; + + fn register_tables(client_cache: &mut __sdk::ClientCache) { + my_player_table::register_table(client_cache); + my_player_and_level_table::register_table(client_cache); + player_table::register_table(client_cache); + player_level_table::register_table(client_cache); + players_at_level_0_table::register_table(client_cache); + } +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/my_player_and_level_table.rs b/sdks/rust/tests/view-client/src/module_bindings/my_player_and_level_table.rs new file mode 100644 index 00000000000..b344dd9eeff --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/my_player_and_level_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::player_and_level_type::PlayerAndLevel; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `my_player_and_level`. +/// +/// Obtain a handle from the [`MyPlayerAndLevelTableAccess::my_player_and_level`] method on [`super::RemoteTables`], +/// like `ctx.db.my_player_and_level()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.my_player_and_level().on_insert(...)`. +pub struct MyPlayerAndLevelTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `my_player_and_level`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait MyPlayerAndLevelTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`MyPlayerAndLevelTableHandle`], which mediates access to the table `my_player_and_level`. + fn my_player_and_level(&self) -> MyPlayerAndLevelTableHandle<'_>; +} + +impl MyPlayerAndLevelTableAccess for super::RemoteTables { + fn my_player_and_level(&self) -> MyPlayerAndLevelTableHandle<'_> { + MyPlayerAndLevelTableHandle { + imp: self.imp.get_table::("my_player_and_level"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct MyPlayerAndLevelInsertCallbackId(__sdk::CallbackId); +pub struct MyPlayerAndLevelDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for MyPlayerAndLevelTableHandle<'ctx> { + type Row = PlayerAndLevel; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = MyPlayerAndLevelInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> MyPlayerAndLevelInsertCallbackId { + MyPlayerAndLevelInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: MyPlayerAndLevelInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = MyPlayerAndLevelDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> MyPlayerAndLevelDeleteCallbackId { + MyPlayerAndLevelDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: MyPlayerAndLevelDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("my_player_and_level"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/my_player_table.rs b/sdks/rust/tests/view-client/src/module_bindings/my_player_table.rs new file mode 100644 index 00000000000..50904a7a156 --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/my_player_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::player_type::Player; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `my_player`. +/// +/// Obtain a handle from the [`MyPlayerTableAccess::my_player`] method on [`super::RemoteTables`], +/// like `ctx.db.my_player()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.my_player().on_insert(...)`. +pub struct MyPlayerTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `my_player`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait MyPlayerTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`MyPlayerTableHandle`], which mediates access to the table `my_player`. + fn my_player(&self) -> MyPlayerTableHandle<'_>; +} + +impl MyPlayerTableAccess for super::RemoteTables { + fn my_player(&self) -> MyPlayerTableHandle<'_> { + MyPlayerTableHandle { + imp: self.imp.get_table::("my_player"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct MyPlayerInsertCallbackId(__sdk::CallbackId); +pub struct MyPlayerDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for MyPlayerTableHandle<'ctx> { + type Row = Player; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = MyPlayerInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> MyPlayerInsertCallbackId { + MyPlayerInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: MyPlayerInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = MyPlayerDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> MyPlayerDeleteCallbackId { + MyPlayerDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: MyPlayerDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("my_player"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/player_and_level_type.rs b/sdks/rust/tests/view-client/src/module_bindings/player_and_level_type.rs new file mode 100644 index 00000000000..0feaea0053b --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/player_and_level_type.rs @@ -0,0 +1,17 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct PlayerAndLevel { + pub entity_id: u64, + pub identity: __sdk::Identity, + pub level: u64, +} + +impl __sdk::InModule for PlayerAndLevel { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/player_level_table.rs b/sdks/rust/tests/view-client/src/module_bindings/player_level_table.rs new file mode 100644 index 00000000000..0b6ded6e0d5 --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/player_level_table.rs @@ -0,0 +1,126 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::player_level_type::PlayerLevel; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `player_level`. +/// +/// Obtain a handle from the [`PlayerLevelTableAccess::player_level`] method on [`super::RemoteTables`], +/// like `ctx.db.player_level()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.player_level().on_insert(...)`. +pub struct PlayerLevelTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `player_level`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait PlayerLevelTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`PlayerLevelTableHandle`], which mediates access to the table `player_level`. + fn player_level(&self) -> PlayerLevelTableHandle<'_>; +} + +impl PlayerLevelTableAccess for super::RemoteTables { + fn player_level(&self) -> PlayerLevelTableHandle<'_> { + PlayerLevelTableHandle { + imp: self.imp.get_table::("player_level"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct PlayerLevelInsertCallbackId(__sdk::CallbackId); +pub struct PlayerLevelDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for PlayerLevelTableHandle<'ctx> { + type Row = PlayerLevel; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = PlayerLevelInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayerLevelInsertCallbackId { + PlayerLevelInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: PlayerLevelInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = PlayerLevelDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayerLevelDeleteCallbackId { + PlayerLevelDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: PlayerLevelDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("player_level"); + _table.add_unique_constraint::("entity_id", |row| &row.entity_id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} + +/// Access to the `entity_id` unique index on the table `player_level`, +/// which allows point queries on the field of the same name +/// via the [`PlayerLevelEntityIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.player_level().entity_id().find(...)`. +pub struct PlayerLevelEntityIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> PlayerLevelTableHandle<'ctx> { + /// Get a handle on the `entity_id` unique index on the table `player_level`. + pub fn entity_id(&self) -> PlayerLevelEntityIdUnique<'ctx> { + PlayerLevelEntityIdUnique { + imp: self.imp.get_unique_constraint::("entity_id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> PlayerLevelEntityIdUnique<'ctx> { + /// Find the subscribed row whose `entity_id` column value is equal to `col_val`, + /// if such a row is present in the client cache. + pub fn find(&self, col_val: &u64) -> Option { + self.imp.find(col_val) + } +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/player_level_type.rs b/sdks/rust/tests/view-client/src/module_bindings/player_level_type.rs new file mode 100644 index 00000000000..1bb1f7555ee --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/player_level_type.rs @@ -0,0 +1,16 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct PlayerLevel { + pub entity_id: u64, + pub level: u64, +} + +impl __sdk::InModule for PlayerLevel { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/player_table.rs b/sdks/rust/tests/view-client/src/module_bindings/player_table.rs new file mode 100644 index 00000000000..f33ba4aa8a7 --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/player_table.rs @@ -0,0 +1,173 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::player_type::Player; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `player`. +/// +/// Obtain a handle from the [`PlayerTableAccess::player`] method on [`super::RemoteTables`], +/// like `ctx.db.player()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.player().on_insert(...)`. +pub struct PlayerTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `player`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait PlayerTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`PlayerTableHandle`], which mediates access to the table `player`. + fn player(&self) -> PlayerTableHandle<'_>; +} + +impl PlayerTableAccess for super::RemoteTables { + fn player(&self) -> PlayerTableHandle<'_> { + PlayerTableHandle { + imp: self.imp.get_table::("player"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct PlayerInsertCallbackId(__sdk::CallbackId); +pub struct PlayerDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for PlayerTableHandle<'ctx> { + type Row = Player; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = PlayerInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayerInsertCallbackId { + PlayerInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: PlayerInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = PlayerDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayerDeleteCallbackId { + PlayerDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: PlayerDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("player"); + _table.add_unique_constraint::("entity_id", |row| &row.entity_id); + _table.add_unique_constraint::<__sdk::Identity>("identity", |row| &row.identity); +} +pub struct PlayerUpdateCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableWithPrimaryKey for PlayerTableHandle<'ctx> { + type UpdateCallbackId = PlayerUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> PlayerUpdateCallbackId { + PlayerUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: PlayerUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} + +/// Access to the `entity_id` unique index on the table `player`, +/// which allows point queries on the field of the same name +/// via the [`PlayerEntityIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.player().entity_id().find(...)`. +pub struct PlayerEntityIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> PlayerTableHandle<'ctx> { + /// Get a handle on the `entity_id` unique index on the table `player`. + pub fn entity_id(&self) -> PlayerEntityIdUnique<'ctx> { + PlayerEntityIdUnique { + imp: self.imp.get_unique_constraint::("entity_id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> PlayerEntityIdUnique<'ctx> { + /// Find the subscribed row whose `entity_id` column value is equal to `col_val`, + /// if such a row is present in the client cache. + pub fn find(&self, col_val: &u64) -> Option { + self.imp.find(col_val) + } +} + +/// Access to the `identity` unique index on the table `player`, +/// which allows point queries on the field of the same name +/// via the [`PlayerIdentityUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.player().identity().find(...)`. +pub struct PlayerIdentityUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> PlayerTableHandle<'ctx> { + /// Get a handle on the `identity` unique index on the table `player`. + pub fn identity(&self) -> PlayerIdentityUnique<'ctx> { + PlayerIdentityUnique { + imp: self.imp.get_unique_constraint::<__sdk::Identity>("identity"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> PlayerIdentityUnique<'ctx> { + /// Find the subscribed row whose `identity` column value is equal to `col_val`, + /// if such a row is present in the client cache. + pub fn find(&self, col_val: &__sdk::Identity) -> Option { + self.imp.find(col_val) + } +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/player_type.rs b/sdks/rust/tests/view-client/src/module_bindings/player_type.rs new file mode 100644 index 00000000000..2ab79152871 --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/player_type.rs @@ -0,0 +1,16 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct Player { + pub entity_id: u64, + pub identity: __sdk::Identity, +} + +impl __sdk::InModule for Player { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/view-client/src/module_bindings/players_at_level_0_table.rs b/sdks/rust/tests/view-client/src/module_bindings/players_at_level_0_table.rs new file mode 100644 index 00000000000..4fcc4ed3f87 --- /dev/null +++ b/sdks/rust/tests/view-client/src/module_bindings/players_at_level_0_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::player_type::Player; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `players_at_level_0`. +/// +/// Obtain a handle from the [`PlayersAtLevel0TableAccess::players_at_level_0`] method on [`super::RemoteTables`], +/// like `ctx.db.players_at_level_0()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.players_at_level_0().on_insert(...)`. +pub struct PlayersAtLevel0TableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `players_at_level_0`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait PlayersAtLevel0TableAccess { + #[allow(non_snake_case)] + /// Obtain a [`PlayersAtLevel0TableHandle`], which mediates access to the table `players_at_level_0`. + fn players_at_level_0(&self) -> PlayersAtLevel0TableHandle<'_>; +} + +impl PlayersAtLevel0TableAccess for super::RemoteTables { + fn players_at_level_0(&self) -> PlayersAtLevel0TableHandle<'_> { + PlayersAtLevel0TableHandle { + imp: self.imp.get_table::("players_at_level_0"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct PlayersAtLevel0InsertCallbackId(__sdk::CallbackId); +pub struct PlayersAtLevel0DeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for PlayersAtLevel0TableHandle<'ctx> { + type Row = Player; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = PlayersAtLevel0InsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayersAtLevel0InsertCallbackId { + PlayersAtLevel0InsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: PlayersAtLevel0InsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = PlayersAtLevel0DeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayersAtLevel0DeleteCallbackId { + PlayersAtLevel0DeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: PlayersAtLevel0DeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("players_at_level_0"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +}