Skip to content

Commit ba535f2

Browse files
Move connection events to reducers (#309)
* Move connection events to reducers * More rebase fixups * Avoid double-reference * Filter out special reducers in generate cli This updates filtering of `__init__` to exclude all special reducers, as well as moves the filtering to centralised place before calling language-specific generate command. * Incrememted ABI version number --------- Co-authored-by: Tyler Cloutier <[email protected]>
1 parent de43e96 commit ba535f2

File tree

21 files changed

+146
-406
lines changed

21 files changed

+146
-406
lines changed

crates/bindings-csharp/Codegen/Codegen.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<LangVersion>latest</LangVersion>
88
<Nullable>enable</Nullable>
99
<AssemblyName>SpacetimeDB.Codegen</AssemblyName>
10-
<AssemblyVersion>0.7.0</AssemblyVersion>
10+
<AssemblyVersion>0.7.1</AssemblyVersion>
1111
</PropertyGroup>
1212

1313
<PropertyGroup>

crates/bindings-csharp/Runtime/Module.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ public static class ReducerKind
142142
{
143143
public const string Init = "__init__";
144144
public const string Update = "__update__";
145+
public const string Connect = "__identity_connected__";
146+
public const string Disconnect = "__identity_disconnected__";
145147
}
146148

147149
public interface IReducer

crates/bindings-csharp/Runtime/Runtime.cs

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -276,37 +276,6 @@ public DbEventArgs(byte[] senderIdentity, byte[] senderAddress, ulong timestamp_
276276
}
277277
}
278278

279-
public static event Action<DbEventArgs>? OnConnect;
280-
public static event Action<DbEventArgs>? OnDisconnect;
281-
282-
// Note: this is accessed by C bindings.
283-
private static string? IdentityConnected(byte[] sender_identity, byte[] sender_address, ulong timestamp)
284-
{
285-
try
286-
{
287-
OnConnect?.Invoke(new(sender_identity, sender_address, timestamp));
288-
return null;
289-
}
290-
catch (Exception e)
291-
{
292-
return e.ToString();
293-
}
294-
}
295-
296-
// Note: this is accessed by C bindings.
297-
private static string? IdentityDisconnected(byte[] sender_identity, byte[] sender_address, ulong timestamp)
298-
{
299-
try
300-
{
301-
OnDisconnect?.Invoke(new(sender_identity, sender_address, timestamp));
302-
return null;
303-
}
304-
catch (Exception e)
305-
{
306-
return e.ToString();
307-
}
308-
}
309-
310279
[MethodImpl(MethodImplOptions.InternalCall)]
311280
private extern static void ScheduleReducer(
312281
string name,

crates/bindings-csharp/Runtime/Runtime.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<LangVersion>latest</LangVersion>
66
<Nullable>enable</Nullable>
77
<AssemblyName>SpacetimeDB.Runtime</AssemblyName>
8-
<AssemblyVersion>0.7.0</AssemblyVersion>
8+
<AssemblyVersion>0.7.1</AssemblyVersion>
99
</PropertyGroup>
1010

1111
<PropertyGroup>

crates/bindings-csharp/Runtime/bindings.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,5 +779,5 @@ __preinit__10_init_csharp() {
779779

780780
// __attribute__((export_name("SPACETIME_ABI_VERSION"))) -
781781
// doesn't work on non-functions, must specify on command line
782-
const uint32_t SPACETIME_ABI_VERSION = /* 4.0 */ (4 << 16) | 0;
782+
const uint32_t SPACETIME_ABI_VERSION = /* 5.0 */ (5 << 16) | 0;
783783
const uint8_t SPACETIME_ABI_VERSION_IS_ADDR = 1;

crates/bindings-macro/src/lib.rs

Lines changed: 59 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ fn route_input(input: MacroInput, item: TokenStream) -> syn::Result<TokenStream>
103103
MacroInput::Table => spacetimedb_table(item),
104104
MacroInput::Init => spacetimedb_init(item),
105105
MacroInput::Reducer { repeat } => spacetimedb_reducer(repeat, item),
106-
MacroInput::Connect => spacetimedb_connect_disconnect(item, true),
107-
MacroInput::Disconnect => spacetimedb_connect_disconnect(item, false),
108-
MacroInput::Migrate => spacetimedb_migrate(item),
106+
MacroInput::Connect => spacetimedb_special_reducer("__identity_connected__", item),
107+
MacroInput::Disconnect => spacetimedb_special_reducer("__identity_disconnected__", item),
108+
MacroInput::Migrate => spacetimedb_special_reducer("__migrate__", item),
109109
MacroInput::Index { ty, name, field_names } => spacetimedb_index(ty, name, field_names, item),
110-
MacroInput::Update => spacetimedb_update(item),
110+
MacroInput::Update => spacetimedb_special_reducer("__update__", item),
111111
}
112112
}
113113

@@ -264,16 +264,12 @@ mod kw {
264264

265265
/// Generates a reducer in place of `item`.
266266
fn spacetimedb_reducer(repeat: Option<Duration>, item: TokenStream) -> syn::Result<TokenStream> {
267-
// TODO(kim): Find a better place for these. `core/host/wasm_common.rs` has similar
268-
// definitions, but we can't depend on `core` here.
269-
const RESERVED_REDUCER_NAMES: &[&str] = &["__init__", "__migrate__", "__update__"];
270-
271-
let repeat_dur = repeat.map_or(ReducerExtra::None, ReducerExtra::Repeat);
267+
let repeat_dur = repeat.map_or(ReducerExtra::Schedule, ReducerExtra::Repeat);
272268
let original_function = syn::parse2::<ItemFn>(item)?;
273269

274-
// Extract reducer name, making sure it's not `__init__`.
270+
// Extract reducer name, making sure it's not `__XXX__` as that's the form we reserve for special reducers.
275271
let reducer_name = original_function.sig.ident.to_string();
276-
if RESERVED_REDUCER_NAMES.iter().any(|name| name == &reducer_name) {
272+
if reducer_name.starts_with("__") && reducer_name.ends_with("__") {
277273
return Err(syn::Error::new_spanned(
278274
&original_function.sig.ident,
279275
"reserved reducer name",
@@ -287,13 +283,13 @@ fn spacetimedb_reducer(repeat: Option<Duration>, item: TokenStream) -> syn::Resu
287283
fn spacetimedb_init(item: TokenStream) -> syn::Result<TokenStream> {
288284
let original_function = syn::parse2::<ItemFn>(item)?;
289285

290-
gen_reducer(original_function, "__init__", ReducerExtra::Init)
286+
gen_reducer(original_function, "__init__", ReducerExtra::None)
291287
}
292288

293289
enum ReducerExtra {
294290
None,
291+
Schedule,
295292
Repeat(Duration),
296-
Init,
297293
}
298294

299295
fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtra) -> syn::Result<TokenStream> {
@@ -321,14 +317,18 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr
321317
// }
322318

323319
// Extract all function parameters, except for `self` ones that aren't allowed.
324-
let args = original_function.sig.inputs.iter().map(|x| match x {
325-
// TODO: improve error message!
326-
FnArg::Receiver(_) => panic!(),
327-
FnArg::Typed(x) => x,
328-
});
320+
let typed_args = original_function
321+
.sig
322+
.inputs
323+
.iter()
324+
.map(|arg| match arg {
325+
FnArg::Typed(arg) => Ok(arg),
326+
_ => Err(syn::Error::new_spanned(arg, "expected typed argument")),
327+
})
328+
.collect::<syn::Result<Vec<_>>>()?;
329329

330330
// Extract all function parameter names.
331-
let arg_names = args.clone().map(|arg| {
331+
let opt_arg_names = typed_args.iter().map(|arg| {
332332
if let syn::Pat::Ident(i) = &*arg.pat {
333333
let name = i.ident.to_string();
334334
quote!(Some(#name))
@@ -337,8 +337,7 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr
337337
}
338338
});
339339

340-
// Extract all function parameter types.
341-
let arg_tys = args.map(|arg| &arg.ty);
340+
let arg_tys = typed_args.iter().map(|arg| arg.ty.as_ref()).collect::<Vec<_>>();
342341

343342
// Extract the return type.
344343
let ret_ty = match &original_function.sig.output {
@@ -349,34 +348,49 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr
349348

350349
let register_describer_symbol = format!("__preinit__20_register_describer_{reducer_name}");
351350

352-
let (epilogue, repeater_impl) = match &extra {
353-
ReducerExtra::None | ReducerExtra::Init => (quote!(), quote!()),
354-
ReducerExtra::Repeat(repeat_dur) => {
355-
let repeat_dur = duration_totokens(*repeat_dur);
356-
let epilogue = quote! {
357-
if _res.is_ok() {
358-
spacetimedb::rt::schedule_repeater::<_, _, #func_name>(#func_name)
359-
}
360-
};
361-
let repeater_impl = quote! {
362-
impl spacetimedb::rt::RepeaterInfo for #func_name {
363-
const REPEAT_INTERVAL: ::core::time::Duration = #repeat_dur;
364-
}
365-
};
366-
(epilogue, repeater_impl)
367-
}
368-
};
351+
let mut epilogue = TokenStream::new();
352+
let mut extra_impls = TokenStream::new();
353+
354+
if !matches!(extra, ReducerExtra::None) {
355+
let arg_names = typed_args
356+
.iter()
357+
.enumerate()
358+
.map(|(i, arg)| match &*arg.pat {
359+
syn::Pat::Ident(pat) => pat.ident.clone(),
360+
_ => format_ident!("__arg{}", i),
361+
})
362+
.collect::<Vec<_>>();
363+
364+
extra_impls.extend(quote!(impl #func_name {
365+
pub fn schedule(__time: spacetimedb::Timestamp #(, #arg_names: #arg_tys)*) -> spacetimedb::ScheduleToken<#func_name> {
366+
spacetimedb::rt::schedule(__time, (#(#arg_names,)*))
367+
}
368+
}));
369+
}
370+
371+
if let ReducerExtra::Repeat(repeat_dur) = &extra {
372+
let repeat_dur = duration_totokens(*repeat_dur);
373+
epilogue.extend(quote! {
374+
if _res.is_ok() {
375+
spacetimedb::rt::schedule_repeater::<_, _, #func_name>(#func_name)
376+
}
377+
});
378+
extra_impls.extend(quote! {
379+
impl spacetimedb::rt::RepeaterInfo for #func_name {
380+
const REPEAT_INTERVAL: ::core::time::Duration = #repeat_dur;
381+
}
382+
});
383+
}
369384

370385
let generated_function = quote! {
371-
// NOTE: double-underscoring names here is unnecessary, as Rust macros are hygienic.
372386
fn __reducer(
373387
__sender: spacetimedb::sys::Buffer,
374388
__caller_address: spacetimedb::sys::Buffer,
375389
__timestamp: u64,
376390
__args: &[u8]
377391
) -> spacetimedb::sys::Buffer {
378-
#(spacetimedb::rt::assert_reducerarg::<#arg_tys>();)*
379-
#(spacetimedb::rt::assert_reducerret::<#ret_ty>();)*
392+
#(spacetimedb::rt::assert_reducer_arg::<#arg_tys>();)*
393+
#(spacetimedb::rt::assert_reducer_ret::<#ret_ty>();)*
380394
spacetimedb::rt::invoke_reducer(
381395
#func_name,
382396
__sender,
@@ -395,56 +409,21 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr
395409
}
396410
};
397411

398-
let mut schedule_func_sig = original_function.sig.clone();
399-
let schedule_func_body = {
400-
schedule_func_sig.ident = format_ident!("schedule");
401-
schedule_func_sig.output = syn::ReturnType::Type(
402-
Token![->](Span::call_site()),
403-
Box::new(syn::parse_quote!(spacetimedb::ScheduleToken<#func_name>)),
404-
);
405-
let arg_names = schedule_func_sig.inputs.iter_mut().enumerate().map(|(i, arg)| {
406-
let syn::FnArg::Typed(arg) = arg else { panic!() };
407-
match &mut *arg.pat {
408-
syn::Pat::Ident(id) => {
409-
id.by_ref = None;
410-
id.mutability = None;
411-
id.ident.clone()
412-
}
413-
_ => {
414-
let ident = format_ident!("__arg{}", i);
415-
arg.pat = Box::new(syn::parse_quote!(#ident));
416-
ident
417-
}
418-
}
419-
});
420-
let schedule_args = quote!((#(#arg_names,)*));
421-
let time_arg = format_ident!("__time");
422-
schedule_func_sig
423-
.inputs
424-
.insert(0, syn::parse_quote!(#time_arg: spacetimedb::Timestamp));
425-
quote! {
426-
spacetimedb::rt::schedule(#time_arg, #schedule_args)
427-
}
428-
};
429-
430412
Ok(quote! {
431413
const _: () = {
432414
#generated_describe_function
433415
};
434416
#[allow(non_camel_case_types)]
435417
#vis struct #func_name { _never: ::core::convert::Infallible }
436-
impl #func_name {
437-
#vis #schedule_func_sig { #schedule_func_body }
438-
}
439418
impl spacetimedb::rt::ReducerInfo for #func_name {
440419
const NAME: &'static str = #reducer_name;
441-
const ARG_NAMES: &'static [Option<&'static str>] = &[#(#arg_names),*];
420+
const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*];
442421
const INVOKE: spacetimedb::rt::ReducerFn = {
443422
#generated_function
444423
__reducer
445424
};
446425
}
447-
#repeater_impl
426+
#extra_impls
448427
#original_function
449428
})
450429
}
@@ -880,48 +859,9 @@ fn spacetimedb_index(
880859
Ok(output)
881860
}
882861

883-
fn spacetimedb_migrate(item: TokenStream) -> syn::Result<TokenStream> {
884-
let original_function = syn::parse2::<ItemFn>(item)?;
885-
gen_reducer(original_function, "__migrate__", ReducerExtra::None)
886-
}
887-
888-
fn spacetimedb_update(item: TokenStream) -> syn::Result<TokenStream> {
862+
fn spacetimedb_special_reducer(name: &str, item: TokenStream) -> syn::Result<TokenStream> {
889863
let original_function = syn::parse2::<ItemFn>(item)?;
890-
gen_reducer(original_function, "__update__", ReducerExtra::None)
891-
}
892-
893-
fn spacetimedb_connect_disconnect(item: TokenStream, connect: bool) -> syn::Result<TokenStream> {
894-
let original_function = syn::parse2::<ItemFn>(item)?;
895-
let func_name = &original_function.sig.ident;
896-
let connect_disconnect_symbol = if connect {
897-
"__identity_connected__"
898-
} else {
899-
"__identity_disconnected__"
900-
};
901-
902-
let emission = quote! {
903-
const _: () = {
904-
#[export_name = #connect_disconnect_symbol]
905-
extern "C" fn __connect_disconnect(
906-
__sender: spacetimedb::sys::Buffer,
907-
__caller_address: spacetimedb::sys::Buffer,
908-
__timestamp: u64,
909-
) -> spacetimedb::sys::Buffer {
910-
spacetimedb::rt::invoke_connection_func(#func_name, __sender, __caller_address, __timestamp)
911-
}
912-
};
913-
914-
#original_function
915-
};
916-
917-
if std::env::var("PROC_MACRO_DEBUG").is_ok() {
918-
{
919-
#![allow(clippy::disallowed_macros)]
920-
println!("{}", emission);
921-
}
922-
}
923-
924-
Ok(emission)
864+
gen_reducer(original_function, name, ReducerExtra::None)
925865
}
926866

927867
#[proc_macro]

crates/bindings-sys/src/lib.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use alloc::boxed::Box;
3434
/// can run a module declaring `X.Y` if and only if `X == A && Y <= B`.
3535
/// So, the minor version is intended for backwards-compatible changes, e.g. adding a new function,
3636
/// and the major version is for fully breaking changes.
37-
pub const ABI_VERSION: u32 = 0x0004_0000;
37+
pub const ABI_VERSION: u32 = 0x0005_0000;
3838

3939
/// Provides a raw set of sys calls which abstractions can be built atop of.
4040
pub mod raw {
@@ -395,10 +395,6 @@ pub mod raw {
395395
/// Required. id is an index into the `ModuleDef.reducers` returned from `__describe_module__`.
396396
/// args is a bsatn-encoded product value defined by the schema at `reducers[id]`.
397397
fn __call_reducer__(id: usize, sender: Identity, timestamp: Timestamp, args: Buffer) -> Result;
398-
/// Optional. Called when a client connects to the database.
399-
fn __identity_connected__(sender: Identity, timestamp: Timestamp) -> Result;
400-
/// Optional. Called when a client disconnects to the database.
401-
fn __identity_disconnected__(sender: Identity, timestamp: Timestamp) -> Result;
402398
/// Currently unused?
403399
fn __migrate_database__XXXX(sender: Identity, timestamp: Timestamp, something: Buffer) -> Result;
404400
}

crates/bindings/src/rt.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,6 @@ pub fn create_index(index_name: &str, table_id: u32, index_type: sys::raw::Index
6767
cvt_result(result.map_err(cvt_errno))
6868
}
6969

70-
/// Runs the on-connect function `f` provided with a new reducer context
71-
/// created from `sender` and `timestamp`.
72-
pub fn invoke_connection_func<R: ReducerResult>(
73-
f: impl Fn(ReducerContext) -> R,
74-
sender: Buffer,
75-
client_address: Buffer,
76-
timestamp: u64,
77-
) -> Buffer {
78-
let ctx = assemble_context(sender, timestamp, client_address);
79-
80-
let res = with_timestamp_set(ctx.timestamp, || f(ctx).into_result());
81-
cvt_result(res)
82-
}
83-
8470
/// Creates a reducer context from the given `sender`, `timestamp` and `client_address`.
8571
///
8672
/// `sender` must contain 32 bytes, from which we will read an `Identity`.
@@ -205,9 +191,9 @@ pub trait ReducerArg<'de> {}
205191
impl<'de, T: Deserialize<'de>> ReducerArg<'de> for T {}
206192
impl ReducerArg<'_> for ReducerContext {}
207193
/// Assert that `T: ReducerArg`.
208-
pub fn assert_reducerarg<'de, T: ReducerArg<'de>>() {}
194+
pub fn assert_reducer_arg<'de, T: ReducerArg<'de>>() {}
209195
/// Assert that `T: ReducerResult`.
210-
pub fn assert_reducerret<T: ReducerResult>() {}
196+
pub fn assert_reducer_ret<T: ReducerResult>() {}
211197
/// Assert that `T: TableType`.
212198
pub const fn assert_table<T: TableType>() {}
213199

0 commit comments

Comments
 (0)