@@ -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`.
266266fn 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
287283fn 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
293289enum ReducerExtra {
294290 None ,
291+ Schedule ,
295292 Repeat ( Duration ) ,
296- Init ,
297293}
298294
299295fn 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]
0 commit comments