@@ -275,6 +275,41 @@ impl IndexArg {
275275 }
276276}
277277
278+ enum AccessorType {
279+ Read ,
280+ ReadWrite ,
281+ }
282+
283+ impl AccessorType {
284+ fn unique ( & self ) -> proc_macro2:: TokenStream {
285+ match self {
286+ AccessorType :: Read => quote ! ( spacetimedb:: UniqueColumnReadOnly ) ,
287+ AccessorType :: ReadWrite => quote ! ( spacetimedb:: UniqueColumn ) ,
288+ }
289+ }
290+
291+ fn range ( & self ) -> proc_macro2:: TokenStream {
292+ match self {
293+ AccessorType :: Read => quote ! ( spacetimedb:: RangedIndexReadOnly ) ,
294+ AccessorType :: ReadWrite => quote ! ( spacetimedb:: RangedIndex ) ,
295+ }
296+ }
297+
298+ fn unique_doc_typename ( & self ) -> & ' static str {
299+ match self {
300+ AccessorType :: Read => "UniqueColumnReadOnly" ,
301+ AccessorType :: ReadWrite => "UniqueColumn" ,
302+ }
303+ }
304+
305+ fn range_doc_typename ( & self ) -> & ' static str {
306+ match self {
307+ AccessorType :: Read => "RangedIndexReadOnly" ,
308+ AccessorType :: ReadWrite => "RangedIndex" ,
309+ }
310+ }
311+ }
312+
278313struct ValidatedIndex < ' a > {
279314 index_name : String ,
280315 accessor_name : & ' a Ident ,
@@ -312,46 +347,71 @@ impl ValidatedIndex<'_> {
312347 } )
313348 }
314349
315- fn accessor ( & self , vis : & syn:: Visibility , row_type_ident : & Ident ) -> TokenStream {
350+ fn accessor (
351+ & self ,
352+ vis : & syn:: Visibility ,
353+ row_type_ident : & Ident ,
354+ tbl_type_ident : & Ident ,
355+ flavor : AccessorType ,
356+ ) -> TokenStream {
316357 let cols = match & self . kind {
317358 ValidatedIndexType :: BTree { cols } => & * * cols,
318359 ValidatedIndexType :: Direct { col } => slice:: from_ref ( col) ,
319360 } ;
320361 if self . is_unique {
321362 assert_eq ! ( cols. len( ) , 1 ) ;
322- let col = cols[ 0 ] ;
323- self . accessor_unique ( col, row_type_ident)
363+ self . unique_accessor ( cols[ 0 ] , row_type_ident, tbl_type_ident, flavor)
324364 } else {
325- self . accessor_general ( vis, row_type_ident, cols)
365+ self . range_accessor ( vis, row_type_ident, tbl_type_ident , cols, flavor )
326366 }
327367 }
328368
329- fn accessor_unique ( & self , col : & Column < ' _ > , row_type_ident : & Ident ) -> TokenStream {
369+ fn unique_accessor (
370+ & self ,
371+ col : & Column < ' _ > ,
372+ row_type_ident : & Ident ,
373+ tbl_type_ident : & Ident ,
374+ flavor : AccessorType ,
375+ ) -> TokenStream {
330376 let index_ident = self . accessor_name ;
331377 let vis = col. vis ;
332378 let col_ty = col. ty ;
333379 let column_ident = col. ident ;
334380
381+ let unique_ty = flavor. unique ( ) ;
382+ let tbl_token = quote ! ( #tbl_type_ident) ;
383+ let doc_type = flavor. unique_doc_typename ( ) ;
384+
335385 let doc = format ! (
336- "Gets the [`UniqueColumn `][spacetimedb::UniqueColumn ] for the \
386+ "Gets the [`{doc_type} `][spacetimedb::{doc_type} ] for the \
337387 [`{column_ident}`][{row_type_ident}::{column_ident}] column."
338388 ) ;
339389 quote ! {
340390 #[ doc = #doc]
341- #vis fn #column_ident( & self ) -> spacetimedb :: UniqueColumn < Self , #col_ty, __indices:: #index_ident> {
342- spacetimedb :: UniqueColumn :: __NEW
391+ #vis fn #column_ident( & self ) -> #unique_ty<#tbl_token , #col_ty, __indices:: #index_ident> {
392+ #unique_ty :: __NEW
343393 }
344394 }
345395 }
346396
347- fn accessor_general ( & self , vis : & syn:: Visibility , row_type_ident : & Ident , cols : & [ & Column < ' _ > ] ) -> TokenStream {
397+ fn range_accessor (
398+ & self ,
399+ vis : & syn:: Visibility ,
400+ row_type_ident : & Ident ,
401+ tbl_type_ident : & Ident ,
402+ cols : & [ & Column < ' _ > ] ,
403+ flavor : AccessorType ,
404+ ) -> TokenStream {
348405 let index_ident = self . accessor_name ;
349- let col_tys = cols. iter ( ) . map ( |col| col. ty ) ;
406+ let col_tys = cols. iter ( ) . map ( |c| c. ty ) ;
407+
408+ let range_ty = flavor. range ( ) ;
409+ let tbl_token = quote ! ( #tbl_type_ident) ;
410+ let doc_type = flavor. range_doc_typename ( ) ;
411+
350412 let mut doc = format ! (
351- "Gets the `{index_ident}` [`RangedIndex`][spacetimedb::RangedIndex] as defined \
352- on this table. \n \
353- \n \
354- This B-tree index is defined on the following columns, in order:\n "
413+ "Gets the `{index_ident}` [`{doc_type}`][spacetimedb::{doc_type}] as defined \
414+ on this table.\n \n This B-tree index is defined on the following columns, in order:\n "
355415 ) ;
356416 for col in cols {
357417 use std:: fmt:: Write ;
@@ -363,10 +423,11 @@ impl ValidatedIndex<'_> {
363423 )
364424 . unwrap ( ) ;
365425 }
426+
366427 quote ! {
367428 #[ doc = #doc]
368- #vis fn #index_ident( & self ) -> spacetimedb :: RangedIndex < Self , ( #( #col_tys, ) * ) , __indices:: #index_ident> {
369- spacetimedb :: RangedIndex :: __NEW
429+ #vis fn #index_ident( & self ) -> #range_ty<#tbl_token , ( #( #col_tys, ) * ) , __indices:: #index_ident> {
430+ #range_ty :: __NEW
370431 }
371432 }
372433 }
@@ -521,6 +582,7 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R
521582
522583 let original_struct_ident = sats_ty. ident ;
523584 let table_ident = & args. name ;
585+ let view_trait_ident = format_ident ! ( "{}__view" , table_ident) ;
524586 let table_name = table_ident. unraw ( ) . to_string ( ) ;
525587 let sats:: SatsTypeData :: Product ( fields) = & sats_ty. data else {
526588 return Err ( syn:: Error :: new ( Span :: call_site ( ) , "spacetimedb table must be a struct" ) ) ;
@@ -656,9 +718,15 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R
656718 indices. sort_by_key ( |index| !index. is_unique ) ;
657719
658720 let tablehandle_ident = format_ident ! ( "{}__TableHandle" , table_ident) ;
721+ let viewhandle_ident = format_ident ! ( "{}__ViewHandle" , table_ident) ;
659722
660723 let index_descs = indices. iter ( ) . map ( |index| index. desc ( ) ) ;
661- let index_accessors = indices. iter ( ) . map ( |index| index. accessor ( vis, original_struct_ident) ) ;
724+ let index_accessors_rw = indices
725+ . iter ( )
726+ . map ( |index| index. accessor ( vis, original_struct_ident, & tablehandle_ident, AccessorType :: ReadWrite ) ) ;
727+ let index_accessors_ro = indices
728+ . iter ( )
729+ . map ( |index| index. accessor ( vis, original_struct_ident, & tablehandle_ident, AccessorType :: Read ) ) ;
662730 let index_marker_types = indices. iter ( ) . map ( |index| index. marker_type ( vis, & tablehandle_ident) ) ;
663731
664732 // Generate `integrate_generated_columns`
@@ -831,12 +899,31 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R
831899 }
832900 } ;
833901
902+ let trait_def_view = quote_spanned ! { table_ident. span( ) =>
903+ #[ allow( non_camel_case_types, dead_code) ]
904+ #vis trait #view_trait_ident {
905+ fn #table_ident( & self ) -> & #viewhandle_ident;
906+ }
907+ impl #view_trait_ident for spacetimedb:: LocalReadOnly {
908+ #[ inline]
909+ fn #table_ident( & self ) -> & #viewhandle_ident {
910+ & #viewhandle_ident { }
911+ }
912+ }
913+ } ;
914+
834915 let tablehandle_def = quote ! {
835916 #[ allow( non_camel_case_types) ]
836917 #[ non_exhaustive]
837918 #vis struct #tablehandle_ident { }
838919 } ;
839920
921+ let viewhandle_def = quote ! {
922+ #[ allow( non_camel_case_types) ]
923+ #[ non_exhaustive]
924+ #vis struct #viewhandle_ident { }
925+ } ;
926+
840927 let emission = quote ! {
841928 const _: ( ) = {
842929 #( let _ = <#field_types as spacetimedb:: rt:: TableColumn >:: _ITEM; ) *
@@ -845,12 +932,18 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R
845932 } ;
846933
847934 #trait_def
935+ #trait_def_view
848936
849937 #tablehandle_def
938+ #viewhandle_def
850939
851940 const _: ( ) = {
852941 impl #tablehandle_ident {
853- #( #index_accessors) *
942+ #( #index_accessors_rw) *
943+ }
944+
945+ impl #viewhandle_ident {
946+ #( #index_accessors_ro) *
854947 }
855948
856949 #tabletype_impl
0 commit comments