mod bind;
mod register;
use crate::connection::{StdbConnection, StdbConnectionState};
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::{Resource, World};
use bevy_state::prelude::OnEnter;
pub(crate) use bind::{EventTableBinder, TableBinder, TableWithoutPkBinder, ViewBinder};
pub(crate) use register::*;
use spacetimedb_sdk::__codegen::{DbConnection, DbContext, SpacetimeModule};
use std::sync::Arc;
pub(crate) type TableRegistrationCallback = dyn Fn(&mut App) + Send + Sync;
pub(crate) type TableBindCallback<C> =
dyn for<'db> Fn(&World, &'db <C as DbContext>::DbView) + Send + Sync;
#[derive(Resource)]
struct StdbTableConfig<
C: DbConnection<Module = M> + DbContext + Send + Sync,
M: SpacetimeModule<DbConnection = C>,
> {
table_bindings: Vec<Arc<TableBindCallback<C>>>,
}
pub(crate) struct StdbTablePlugin<C, M>
where
C: DbConnection<Module = M> + DbContext + Send + Sync + 'static,
M: SpacetimeModule<DbConnection = C>,
{
table_registrations: Vec<Arc<TableRegistrationCallback>>,
table_bindings: Vec<Arc<TableBindCallback<C>>>,
}
impl<C, M> StdbTablePlugin<C, M>
where
C: DbConnection<Module = M> + DbContext + Send + Sync + 'static,
M: SpacetimeModule<DbConnection = C>,
{
pub fn new(
table_bindings: Vec<Arc<TableBindCallback<C>>>,
table_registrations: Vec<Arc<TableRegistrationCallback>>,
) -> Self {
Self {
table_bindings,
table_registrations,
}
}
}
impl<C, M> Plugin for StdbTablePlugin<C, M>
where
C: DbConnection<Module = M> + DbContext + Send + Sync + 'static,
M: SpacetimeModule<DbConnection = C> + 'static,
{
fn build(&self, app: &mut App) {
for register in &self.table_registrations {
register(app);
}
app.insert_resource(StdbTableConfig::<C, M> {
table_bindings: self.table_bindings.clone(),
});
app.add_systems(
OnEnter(StdbConnectionState::Connected),
on_connected_bind::<C, M>,
);
}
}
fn on_connected_bind<
C: DbConnection<Module = M> + DbContext + Send + Sync,
M: SpacetimeModule<DbConnection = C>,
>(
world: &mut World,
) {
let config = world
.get_resource::<StdbTableConfig<C, M>>()
.expect("StdbTableConfig should exist before Connected bind phase");
let conn = world
.get_resource::<StdbConnection<C>>()
.expect("StdbConnection should exist before Connected bind phase");
let db = conn.db();
for bind in &config.table_bindings {
bind(&*world, db);
}
}