Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions crates/codegen/src/rust.rs
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trait changes mean we need updated codegen too (breaking).

Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,15 @@ pub struct {insert_callback_id}(__sdk::CallbackId);
write!(
out,
"
impl<'ctx> __sdk::EventTable for {table_handle}<'ctx> {{
impl<'ctx> __sdk::TableLike for {table_handle}<'ctx> {{
type Row = {row_type};
type EventContext = super::EventContext;

fn count(&self) -> u64 {{ self.imp.count() }}
fn iter(&self) -> impl Iterator<Item = {row_type}> + '_ {{ self.imp.iter() }}
}}

impl<'ctx> __sdk::WithInsert for {table_handle}<'ctx> {{
type InsertCallbackId = {insert_callback_id};

fn on_insert(
Expand All @@ -203,6 +205,8 @@ impl<'ctx> __sdk::EventTable for {table_handle}<'ctx> {{
self.imp.remove_on_insert(callback.0)
}}
}}

impl<'ctx> __sdk::EventTable for {table_handle}<'ctx> {{}}
Comment on lines +208 to +209
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these empty impls? Maybe now Table can just be removed entirely?

"
);
} else {
Expand All @@ -213,13 +217,15 @@ impl<'ctx> __sdk::EventTable for {table_handle}<'ctx> {{
out,
"pub struct {delete_callback_id}(__sdk::CallbackId);

impl<'ctx> __sdk::Table for {table_handle}<'ctx> {{
impl<'ctx> __sdk::TableLike for {table_handle}<'ctx> {{
type Row = {row_type};
type EventContext = super::EventContext;

fn count(&self) -> u64 {{ self.imp.count() }}
fn iter(&self) -> impl Iterator<Item = {row_type}> + '_ {{ self.imp.iter() }}
}}

impl<'ctx> __sdk::WithInsert for {table_handle}<'ctx> {{
type InsertCallbackId = {insert_callback_id};

fn on_insert(
Expand All @@ -232,7 +238,9 @@ impl<'ctx> __sdk::Table for {table_handle}<'ctx> {{
fn remove_on_insert(&self, callback: {insert_callback_id}) {{
self.imp.remove_on_insert(callback.0)
}}
}}

impl<'ctx> __sdk::WithDelete for {table_handle}<'ctx> {{
type DeleteCallbackId = {delete_callback_id};

fn on_delete(
Expand All @@ -246,6 +254,8 @@ impl<'ctx> __sdk::Table for {table_handle}<'ctx> {{
self.imp.remove_on_delete(callback.0)
}}
}}

impl<'ctx> __sdk::Table for {table_handle}<'ctx> {{}}
"
);

Expand All @@ -258,7 +268,7 @@ impl<'ctx> __sdk::Table for {table_handle}<'ctx> {{
"
pub struct {update_callback_id}(__sdk::CallbackId);

impl<'ctx> __sdk::TableWithPrimaryKey for {table_handle}<'ctx> {{
impl<'ctx> __sdk::WithUpdate for {table_handle}<'ctx> {{
type UpdateCallbackId = {update_callback_id};

fn on_update(
Expand All @@ -272,6 +282,8 @@ impl<'ctx> __sdk::TableWithPrimaryKey for {table_handle}<'ctx> {{
self.imp.remove_on_update(callback.0)
}}
}}

impl<'ctx> __sdk::TableWithPrimaryKey for {table_handle}<'ctx> {{}}
"
);
}
Expand Down Expand Up @@ -1877,7 +1889,7 @@ impl<Ctx: __sdk::DbContext<
out,
"EventContext",
Some("__sdk::Event<Reducer>"),
"[`__sdk::Table::on_insert`], [`__sdk::Table::on_delete`] and [`__sdk::TableWithPrimaryKey::on_update`] callbacks",
"[`__sdk::WithInsert::on_insert`], [`__sdk::WithDelete::on_delete`] and [`__sdk::WithUpdate::on_update`] callbacks",
Some("[`__sdk::Event`]"),
);

Expand Down
4 changes: 2 additions & 2 deletions sdks/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub use db_connection::DbConnectionBuilder;
pub use db_context::DbContext;
pub use error::{Error, Result};
pub use event::{Event, ReducerEvent, Status};
pub use table::{EventTable, Table, TableWithPrimaryKey};
pub use table::{EventTable, Table, TableLike, TableWithPrimaryKey, WithDelete, WithInsert, WithUpdate};

pub use spacetime_module::SubscriptionHandle;
pub use spacetimedb_client_api_messages::websocket::v1::Compression;
Expand Down Expand Up @@ -62,7 +62,7 @@ pub mod __codegen {
pub use crate::subscription::{OnEndedCallback, SubscriptionBuilder, SubscriptionHandleImpl};
pub use crate::{
ConnectionId, DbConnectionBuilder, DbContext, Event, EventTable, Identity, ReducerEvent, ScheduleAt, Table,
TableWithPrimaryKey, TimeDuration, Timestamp, Uuid,
TableLike, TableWithPrimaryKey, TimeDuration, Timestamp, Uuid, WithDelete, WithInsert, WithUpdate,
};
}

Expand Down
84 changes: 42 additions & 42 deletions sdks/rust/src/table.rs
Copy link
Copy Markdown
Author

@onx2 onx2 Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main change to functionality. I wanted to have access to specific traits so in bevy_stdb I can go from:

Before/after

fn bind_insert<TRow, TTable>(world: &World, table: &TTable)
where
    TRow: Send + Sync + Clone + InModule + 'static,
    RowEvent<TRow>: Send + Sync,
-   TTable: Table<
+   TTable: WithInsert<
            Row = TRow,
            EventContext = <<TRow as InModule>::Module as SpacetimeModule>::EventContext,
        >,
    TTable::EventContext: AbstractEventContext<Event = RowEvent<TRow>>,
{
    let sender = channel_sender::<InsertMessage<TRow>>(world);
    table.on_insert(move |ctx, row| {
        let _ = sender.send(InsertMessage {
            event: ctx.event().clone(),
            row: row.clone(),
        });
    });
}

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! The [`Table`] and [`TableWithPrimaryKey`] traits,
//! The [`TableLike`], [`Table`], [`TableWithPrimaryKey`], and [`EventTable`] traits,
//! together with the [`WithInsert`], [`WithDelete`], and [`WithUpdate`] capability traits,
//! which allow certain simple queries of the client cache,
//! and expose row callbacks which run when subscribed rows are inserted, deleted or updated.
//!
Expand All @@ -7,12 +8,10 @@
//! which mediate access to tables in the client cache.
//! Obtain a table handle by calling a method on `ctx.db`, where `ctx` is a `DbConnection` or `EventContext`.

/// Trait implemented by table handles, which mediate access to tables in the client cache.
/// Common functionality shared by table-like handles.
///
/// Obtain a table handle by calling a method on `ctx.db`, where `ctx` is a `DbConnection` or `EventContext`.
///
/// For persistent (non-event) tables only. See [`EventTable`] for transient event tables.
pub trait Table {
pub trait TableLike {
/// The type of rows stored in this table.
type Row: 'static;

Expand All @@ -24,20 +23,32 @@ pub trait Table {

/// An iterator over all the subscribed rows in the client cache.
fn iter(&self) -> impl Iterator<Item = Self::Row> + '_;
}

/// Capability trait for table-like handles which support insert callbacks.
pub trait WithInsert: TableLike {
type InsertCallbackId;
/// Register a callback to run whenever a subscribed row is inserted into the client cache.

/// Register a callback to run whenever a row is observed as inserted by this table handle.
///
/// For persistent tables, this means a subscribed row is inserted into the client cache.
/// For event tables, this means an event row is delivered.
///
/// The returned [`Self::InsertCallbackId`] can be passed to [`Self::remove_on_insert`]
/// to cancel the callback.
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> Self::InsertCallbackId;

/// Cancel a callback previously registered by [`Self::on_insert`], causing it not to run in the future.
fn remove_on_insert(&self, callback: Self::InsertCallbackId);
}

/// Capability trait for table-like handles which support delete callbacks.
pub trait WithDelete: TableLike {
type DeleteCallbackId;

/// Register a callback to run whenever a subscribed row is deleted from the client cache.
///
/// The returned [`Self::DeleteCallbackId`] can be passed to [`Self::remove_on_delete`]
Expand All @@ -46,22 +57,15 @@ pub trait Table {
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> Self::DeleteCallbackId;

/// Cancel a callback previously registered by [`Self::on_delete`], causing it not to run in the future.
fn remove_on_delete(&self, callback: Self::DeleteCallbackId);
}

/// Subtrait of [`Table`] implemented only by tables with a column designated as a primary key,
/// which allows the SDK to identify updates.
///
/// SpacetimeDB does not have a special notion of updates as a primitive operation.
/// Instead, an update is a delete followed by an insert
/// of rows with the same primary key within the same transaction.
/// This means that the module's calls to `ctx.db.some_table().unique_column().update(...)`
/// may not result in an update event on the client if `unique_column` is not the primary key,
/// and that clients may observe update events resulting from deletes and inserts by the module
/// without going through such an `update` method.
pub trait TableWithPrimaryKey: Table {
/// Capability trait for table-like handles which support update callbacks.
pub trait WithUpdate: TableLike {
type UpdateCallbackId;

/// Register a callback to run whenever a subscribed row is updated within a transaction,
/// with an old version deleted and a new version inserted with the same primary key.
///
Expand All @@ -74,38 +78,34 @@ pub trait TableWithPrimaryKey: Table {
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
) -> Self::UpdateCallbackId;

/// Cancel a callback previously registered by [`Self::on_update`], causing it not to run in the future.
fn remove_on_update(&self, callback: Self::UpdateCallbackId);
}

/// Trait implemented by persistent table handles, which mediate access to tables in the client cache.
///
/// Obtain a table handle by calling a method on `ctx.db`, where `ctx` is a `DbConnection` or `EventContext`.
///
/// For persistent (non-event) tables only. See [`EventTable`] for transient event tables.
pub trait Table: TableLike + WithInsert + WithDelete {}

/// Subtrait of [`Table`] implemented only by tables with a column designated as a primary key,
/// which allows the SDK to identify updates.
///
/// SpacetimeDB does not have a special notion of updates as a primitive operation.
/// Instead, an update is a delete followed by an insert
/// of rows with the same primary key within the same transaction.
/// This means that the module's calls to `ctx.db.some_table().unique_column().update(...)`
/// may not result in an update event on the client if `unique_column` is not the primary key,
/// and that clients may observe update events resulting from deletes and inserts by the module
/// without going through such an `update` method.
pub trait TableWithPrimaryKey: Table + WithUpdate {}

/// Trait for event tables, whose rows are transient and never persisted in the client cache.
///
/// Event table rows are delivered as inserts but are not stored;
/// only `on_insert` callbacks fire, and `count`/`iter` always reflect an empty table.
///
/// Obtain a table handle by calling a method on `ctx.db`, where `ctx` is a `DbConnection` or `EventContext`.
pub trait EventTable {
/// The type of rows in this table.
type Row: 'static;

/// The `EventContext` type generated for the module which defines this table.
type EventContext;

/// The number of subscribed rows in the client cache (always 0 for event tables).
fn count(&self) -> u64;

/// An iterator over all the subscribed rows in the client cache (always empty for event tables).
fn iter(&self) -> impl Iterator<Item = Self::Row> + '_;

type InsertCallbackId;
/// Register a callback to run whenever a row is inserted.
///
/// The returned [`Self::InsertCallbackId`] can be passed to [`Self::remove_on_insert`]
/// to cancel the callback.
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> Self::InsertCallbackId;
/// Cancel a callback previously registered by [`Self::on_insert`], causing it not to run in the future.
fn remove_on_insert(&self, callback: Self::InsertCallbackId);
}
pub trait EventTable: TableLike + WithInsert {}

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

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

This file was deleted.

Loading