@@ -12,13 +12,15 @@ extern crate core;
1212extern crate proc_macro;
1313
1414use bitflags:: Flags ;
15+ use heck:: ToSnakeCase ;
1516use module:: { derive_deserialize, derive_satstype, derive_serialize} ;
1617use proc_macro:: TokenStream as StdTokenStream ;
1718use proc_macro2:: { Span , TokenStream } ;
1819use quote:: { format_ident, quote, quote_spanned} ;
1920use spacetimedb_primitives:: ColumnAttribute ;
2021use std:: collections:: HashMap ;
2122use std:: time:: Duration ;
23+ use syn:: ext:: IdentExt ;
2224use syn:: meta:: ParseNestedMeta ;
2325use syn:: parse:: { Parse , ParseStream , Parser } ;
2426use syn:: punctuated:: Punctuated ;
@@ -189,8 +191,8 @@ impl LifecycleReducer {
189191 fn reducer_name ( & self ) -> & ' static str {
190192 match self {
191193 Self :: Init => "__init__" ,
192- Self :: ClientConnected => "__client_connected__ " ,
193- Self :: ClientDisconnected => "__client_disconnected__ " ,
194+ Self :: ClientConnected => "__identity_connected__ " ,
195+ Self :: ClientDisconnected => "__identity_disconnected__ " ,
194196 Self :: Update => "__update__" ,
195197 }
196198 }
@@ -316,26 +318,6 @@ fn reducer_impl(args: ReducerArgs, original_function: &ItemFn) -> syn::Result<To
316318 }
317319 } ;
318320
319- // let errmsg = "reducer should have at least 2 arguments: (identity: Identity, timestamp: u64, ...)";
320- // let ([arg1, arg2], args) = validate_reducer_args(&original_function.sig, errmsg)?;
321-
322- // // TODO: better (non-string-based) validation for these
323- // if !matches!(
324- // &*arg1.to_token_stream().to_string(),
325- // "spacetimedb::spacetimedb_sats::hash::Hash" | "Hash"
326- // ) {
327- // return Err(syn::Error::new_spanned(
328- // &arg1,
329- // "1st parameter of a reducer must be of type \'u64\'.",
330- // ));
331- // }
332- // if arg2.to_token_stream().to_string() != "u64" {
333- // return Err(syn::Error::new_spanned(
334- // &arg2,
335- // "2nd parameter of a reducer must be of type \'u64\'.",
336- // ));
337- // }
338-
339321 // Extract all function parameters, except for `self` ones that aren't allowed.
340322 let typed_args = original_function
341323 . sig
@@ -400,11 +382,10 @@ fn reducer_impl(args: ReducerArgs, original_function: &ItemFn) -> syn::Result<To
400382 } )
401383}
402384
403- #[ derive( Default ) ]
404385struct TableArgs {
405386 public : Option < Span > ,
406387 scheduled : Option < Path > ,
407- name : Option < Ident > ,
388+ name : Ident ,
408389 indices : Vec < IndexArg > ,
409390}
410391
@@ -443,9 +424,12 @@ enum IndexType {
443424}
444425
445426impl TableArgs {
446- fn parse ( input : TokenStream ) -> syn:: Result < Self > {
427+ fn parse ( input : TokenStream , struct_ident : & Ident ) -> syn:: Result < Self > {
447428 let mut specified_access = false ;
448- let mut args = TableArgs :: default ( ) ;
429+ let mut public = None ;
430+ let mut scheduled = None ;
431+ let mut name = None ;
432+ let mut indices = Vec :: new ( ) ;
449433 syn:: meta:: parser ( |meta| {
450434 let mut specified_access = || {
451435 if specified_access {
@@ -457,28 +441,40 @@ impl TableArgs {
457441 match_meta ! ( match meta {
458442 sym:: public => {
459443 specified_access( ) ?;
460- args . public = Some ( meta. path. span( ) ) ;
444+ public = Some ( meta. path. span( ) ) ;
461445 }
462446 sym:: private => {
463447 specified_access( ) ?;
464448 }
465449 sym:: name => {
466- check_duplicate( & args . name, & meta) ?;
450+ check_duplicate( & name, & meta) ?;
467451 let value = meta. value( ) ?;
468- args . name = Some ( value. parse( ) ?) ;
452+ name = Some ( value. parse( ) ?) ;
469453 }
470- sym:: index => args . indices. push( IndexArg :: parse_meta( meta) ?) ,
454+ sym:: index => indices. push( IndexArg :: parse_meta( meta) ?) ,
471455 sym:: scheduled => {
472- check_duplicate( & args . scheduled, & meta) ?;
456+ check_duplicate( & scheduled, & meta) ?;
473457 let in_parens;
474458 syn:: parenthesized!( in_parens in meta. input) ;
475- args . scheduled = Some ( in_parens. parse:: <Path >( ) ?) ;
459+ scheduled = Some ( in_parens. parse:: <Path >( ) ?) ;
476460 }
477461 } ) ;
478462 Ok ( ( ) )
479463 } )
480464 . parse2 ( input) ?;
481- Ok ( args)
465+ let name = name. ok_or_else ( || {
466+ let table = struct_ident. to_string ( ) . to_snake_case ( ) ;
467+ syn:: Error :: new (
468+ Span :: call_site ( ) ,
469+ format_args ! ( "must specify table name, e.g. `#[spacetimedb::table(name = {table})]" ) ,
470+ )
471+ } ) ?;
472+ Ok ( TableArgs {
473+ public,
474+ scheduled,
475+ name,
476+ indices,
477+ } )
482478 }
483479}
484480
@@ -586,7 +582,7 @@ impl IndexArg {
586582/// # Example
587583///
588584/// ```ignore
589- /// #[spacetimedb::table(public, name = Users )]
585+ /// #[spacetimedb::table(name = users, public )]
590586/// pub struct User {
591587/// #[auto_inc]
592588/// #[primary_key]
@@ -654,7 +650,7 @@ pub fn table(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {
654650 // put this on the struct so we don't get unknown attribute errors
655651 let extra_attr = quote ! ( #[ derive( spacetimedb:: __TableHelper) ] ) ;
656652 cvt_attr :: < syn:: DeriveInput > ( args, item, extra_attr, |args, item| {
657- let args = TableArgs :: parse ( args) ?;
653+ let args = TableArgs :: parse ( args, & item . ident ) ?;
658654 table_impl ( args, item)
659655 } )
660656}
@@ -717,15 +713,10 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
717713 reducer_type_check ( & item, reducer)
718714 } ) ;
719715
720- let mut sats_ty = module:: sats_type_from_derive ( & item, quote ! ( spacetimedb:: spacetimedb_lib) ) ?;
716+ let sats_ty = module:: sats_type_from_derive ( & item, quote ! ( spacetimedb:: spacetimedb_lib) ) ?;
721717
722718 let original_struct_ident = sats_ty. ident ;
723- // TODO: error on setting sats name for a table
724- let table_name = args
725- . name
726- . map ( |s| s. to_string ( ) )
727- . unwrap_or_else ( || original_struct_ident. to_string ( ) ) ;
728- sats_ty. name . clone_from ( & table_name) ;
719+ let table_name = args. name . unraw ( ) . to_string ( ) ;
729720 let module:: SatsTypeData :: Product ( fields) = & sats_ty. data else {
730721 return Err ( syn:: Error :: new ( Span :: call_site ( ) , "spacetimedb table must be a struct" ) ) ;
731722 } ;
@@ -858,7 +849,7 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
858849
859850 Some ( quote ! {
860851 // TODO: should we expose spacetimedb::query::FilterByIter ?
861- #vis fn #filter_func_ident< ' a> ( #column_ident: & #column_type) -> impl Iterator <Item = Self > {
852+ #vis fn #filter_func_ident( #column_ident: & #column_type) -> impl Iterator <Item = Self > {
862853 spacetimedb:: query:: filter_by_field:: <Self , #column_type, #column_index>( #column_ident)
863854 }
864855 #vis fn #delete_func_ident( #column_ident: & #column_type) -> u32 {
@@ -896,7 +887,7 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
896887
897888 let deserialize_impl = derive_deserialize ( & sats_ty) ;
898889 let serialize_impl = derive_serialize ( & sats_ty) ;
899- let schema_impl = derive_satstype ( & sats_ty, false ) ;
890+ let schema_impl = derive_satstype ( & sats_ty, * original_struct_ident != table_name ) ;
900891 let column_attrs = columns. iter ( ) . map ( |col| {
901892 Ident :: new (
902893 ColumnAttribute :: FLAGS
@@ -1309,7 +1300,7 @@ impl ClosureLike {
13091300/// ```ignore // unfortunately, doctest doesn't work well inside proc-macro
13101301/// use spacetimedb::query;
13111302///
1312- /// #[spacetimedb::table]
1303+ /// #[spacetimedb::table(name = people) ]
13131304/// pub struct Person {
13141305/// name: String,
13151306/// age: u32,
0 commit comments