Skip to content

Commit 39b19f7

Browse files
committed
Impl macro rework
1 parent 607f7ce commit 39b19f7

File tree

36 files changed

+946
-881
lines changed

36 files changed

+946
-881
lines changed

crates/bindings-macro/src/lib.rs

Lines changed: 504 additions & 370 deletions
Large diffs are not rendered by default.

crates/bindings-macro/src/macros.rs

Lines changed: 33 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,43 @@
1-
use std::marker::PhantomData;
2-
3-
use syn::parse::{Lookahead1, Parse, ParseStream};
4-
use syn::token::Token;
1+
pub(crate) fn one_of(options: &[crate::sym::Symbol]) -> String {
2+
match options {
3+
[] => "unexpected attribute".to_owned(),
4+
[a] => {
5+
format!("expected `{a}`")
6+
}
7+
[a, b] => {
8+
format!("expected `{a}` or `{b}`")
9+
}
10+
_ => {
11+
let join = options.join("`, `");
12+
format!("expected one of: `{}`", join)
13+
}
14+
}
15+
}
516

6-
macro_rules! match_tok {
7-
(match $input:ident { $($matches:tt)* }) => {{
8-
use $crate::macros::PeekParse;
9-
let input: syn::parse::ParseStream = $input;
10-
let lookahead = input.lookahead1();
11-
match_tok!(@match lookahead, input { $($matches)* })
17+
macro_rules! match_meta {
18+
(match $meta:ident { $($matches:tt)* }) => {{
19+
let meta: &syn::meta::ParseNestedMeta = &$meta;
20+
match_meta!(@match (), (), meta { $($matches)* })
1221
}};
1322

14-
(@match $lookahead:ident, $input:ident { $binding:tt @ $tok:ty => $body:block $($rest:tt)* }) => {
15-
match_tok!(@case $lookahead, $input, $binding, $tok, $body, { $($rest)* })
23+
(@match $acc:tt, $comparisons:tt, $meta:ident { $sym:path => $body:block $($rest:tt)* }) => {
24+
match_meta!(@case $acc, $comparisons, $meta, _, $sym, $body, { $($rest)* })
1625
};
17-
(@match $lookahead:ident, $input:ident { $tok:ty => $body:block $($rest:tt)* }) => {
18-
match_tok!(@case $lookahead, $input, _, $tok, $body, { $($rest)* })
19-
};
20-
(@match $lookahead:ident, $input:ident { $binding:tt @ $tok:ty => $body:expr, $($rest:tt)* }) => {
21-
match_tok!(@case $lookahead, $input, $binding, $tok, $body, { $($rest)* })
22-
};
23-
(@match $lookahead:ident, $input:ident { $tok:ty => $body:expr, $($rest:tt)* }) => {
24-
match_tok!(@case $lookahead, $input, _, $tok, $body, { $($rest)* })
26+
(@match $acc:tt, $comparisons:tt, $meta:ident { $sym:path => $body:expr, $($rest:tt)* }) => {
27+
match_meta!(@case $acc, $comparisons, $meta, _, $sym, $body, { $($rest)* })
2528
};
2629

27-
(@match $lookahead:ident, $input:ident {}) => {
28-
return Err($lookahead.error())
29-
};
30-
31-
(@case $lookahead:ident, $input:ident, $binding:tt, $tok:ty, $body:expr, { $($rest:tt)* }) => {
32-
if $crate::macros::peekparser::<$tok>().peekparse_peek(&$lookahead, $input) {
33-
let $binding = $crate::macros::peekparser::<$tok>().peekparse_parse($input)?;
34-
$body
35-
} else {
36-
match_tok!(@match $lookahead, $input { $($rest)* })
30+
(@match ($($acc:tt)*), ($($comparisons:expr),*), $meta:ident {}) => {
31+
match () {
32+
$($acc)*
33+
_ => return Err($meta.error($crate::macros::one_of(&[$($comparisons),*]))),
3734
}
3835
};
39-
}
4036

41-
pub fn peekparser<T>() -> &'static PhantomData<T> {
42-
&PhantomData
43-
}
44-
45-
pub trait PeekParse {
46-
type Output;
47-
fn peekparse_peek(&self, lookahead1: &Lookahead1, input: ParseStream) -> bool;
48-
fn peekparse_parse(&self, input: ParseStream) -> syn::Result<Self::Output>;
49-
}
50-
51-
impl<T: Token + Parse> PeekParse for PhantomData<T> {
52-
type Output = T;
53-
fn peekparse_peek(&self, lookahead1: &Lookahead1, _input: ParseStream) -> bool {
54-
lookahead1.peek(|x| -> T { match x {} })
55-
}
56-
fn peekparse_parse(&self, input: ParseStream) -> syn::Result<Self::Output> {
57-
input.parse()
58-
}
59-
}
60-
61-
impl<T1, T2> PeekParse for &PhantomData<(T1, T2)>
62-
where
63-
T1: Token + Parse,
64-
T2: Token + Parse,
65-
{
66-
type Output = (T1, T2);
67-
fn peekparse_peek(&self, lookahead1: &Lookahead1, input: ParseStream) -> bool {
68-
lookahead1.peek(|x| -> T1 { match x {} }) && input.peek2(|x| -> T2 { match x {} })
69-
}
70-
fn peekparse_parse(&self, input: ParseStream) -> syn::Result<Self::Output> {
71-
Ok((input.parse()?, input.parse()?))
72-
}
37+
(@case ($($acc:tt)*), ($($comparisons:expr),*), $meta:ident, $binding:tt, $sym:path, $body:expr, { $($rest:tt)* }) => {
38+
match_meta!(@match (
39+
$($acc)*
40+
_ if $meta.path == $sym => $body,
41+
), ($($comparisons,)* $sym), $meta { $($rest)* })
42+
};
7343
}

crates/bindings-macro/src/module.rs

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ use syn::punctuated::Pair;
77
use syn::spanned::Spanned;
88
use syn::{LitStr, Token};
99

10-
use crate::{check_duplicate_meta, sym};
10+
use crate::{check_duplicate, sym};
1111

1212
pub(crate) struct SatsType<'a> {
1313
pub ident: &'a syn::Ident,
1414
pub generics: &'a syn::Generics,
1515
pub name: String,
1616
pub krate: TokenStream,
17+
// may want to use in the future
18+
#[allow(unused)]
1719
pub original_attrs: &'a [syn::Attribute],
1820
pub data: SatsTypeData<'a>,
19-
pub public: Option<Span>,
20-
pub scheduled: Option<String>,
2121
}
2222

2323
pub(crate) enum SatsTypeData<'a> {
@@ -40,6 +40,7 @@ pub(crate) struct SatsVariant<'a> {
4040
pub name: String,
4141
pub ty: Option<&'a syn::Type>,
4242
pub member: Option<syn::Member>,
43+
// may want to use in the future
4344
#[allow(unused)]
4445
pub original_attrs: &'a [syn::Attribute],
4546
}
@@ -87,37 +88,25 @@ pub(crate) fn extract_sats_type<'a>(
8788
) -> syn::Result<SatsType<'a>> {
8889
let mut name = None;
8990
let mut krate = None;
90-
let mut public = None;
91-
let mut scheduled = None;
9291
for attr in attrs {
93-
if attr.path() != sym::SATS {
92+
if attr.path() != sym::sats {
9493
continue;
9594
}
9695
attr.parse_nested_meta(|meta| {
97-
if meta.path == sym::CRATE {
98-
check_duplicate_meta(&krate, &meta)?;
99-
let value = meta.value()?;
100-
let v = value.call(syn::Path::parse_mod_style)?;
101-
krate = Some(v.into_token_stream());
102-
} else if meta.path == sym::NAME {
103-
check_duplicate_meta(&name, &meta)?;
104-
let value = meta.value()?;
105-
let v = value.parse::<LitStr>()?;
106-
name = Some(v.value());
107-
} else if meta.path == sym::PUBLIC {
108-
check_duplicate_meta(&public, &meta)?;
109-
if !meta.input.is_empty() {
110-
return Err(meta.error("public modifier must be empty"));
96+
match_meta!(match meta {
97+
sym::crate_ => {
98+
check_duplicate(&krate, &meta)?;
99+
let value = meta.value()?;
100+
let v = value.call(syn::Path::parse_mod_style)?;
101+
krate = Some(v.into_token_stream());
111102
}
112-
public = Some(meta.path.span());
113-
} else if meta.path == sym::SCHEDULED {
114-
check_duplicate_meta(&scheduled, &meta)?;
115-
let value = meta.value()?;
116-
let v = value.parse::<LitStr>()?;
117-
scheduled = Some(v.value());
118-
} else {
119-
return Err(meta.error("unknown sats attribute"));
120-
}
103+
sym::name => {
104+
check_duplicate(&name, &meta)?;
105+
let value = meta.value()?;
106+
let v = value.parse::<LitStr>()?;
107+
name = Some(v.value());
108+
}
109+
});
121110
Ok(())
122111
})?;
123112
}
@@ -131,19 +120,9 @@ pub(crate) fn extract_sats_type<'a>(
131120
krate,
132121
original_attrs: attrs,
133122
data,
134-
public,
135-
scheduled,
136123
})
137124
}
138125

139-
/// Ensures that the `public` modifier is not present.
140-
pub(crate) fn ensure_no_public(ty: &SatsType<'_>, mut ts: TokenStream) -> TokenStream {
141-
if let Some(span) = ty.public {
142-
ts.extend(syn::Error::new(span, "the `public` specifier is not allowed in this context").into_compile_error());
143-
}
144-
ts
145-
}
146-
147126
pub(crate) fn derive_satstype(ty: &SatsType<'_>, gen_type_alias: bool) -> TokenStream {
148127
let ty_name = &ty.name;
149128
let name = &ty.ident;

crates/bindings/src/lib.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ pub use rand;
3232
#[cfg(feature = "rand")]
3333
pub use rng::{random, rng, StdbRng};
3434
pub use sats::SpacetimeType;
35-
pub use spacetimedb_bindings_macro::{duration, query, spacetimedb, TableType};
35+
#[doc(hidden)]
36+
pub use spacetimedb_bindings_macro::__TableHelper;
37+
pub use spacetimedb_bindings_macro::{duration, query, reducer, table};
3638
pub use spacetimedb_bindings_sys as sys;
3739
pub use spacetimedb_lib;
3840
pub use spacetimedb_lib::de::{Deserialize, DeserializeOwned};
@@ -469,7 +471,7 @@ pub mod query {
469471
/// according to the column's schema and then `Ord for AlgebraicValue`.
470472
///
471473
/// **NOTE:** Do not use directly.
472-
/// This is exposed as `filter_by_{$field_name}` on types with `#[spacetimedb(table)]`.
474+
/// This is exposed as `filter_by_{$field_name}` on types with `#[spacetimedb::table]`.
473475
#[doc(hidden)]
474476
pub fn filter_by_unique_field<
475477
Table: TableType + FieldAccess<COL_IDX, Field = T>,
@@ -503,7 +505,7 @@ pub mod query {
503505
/// according to the column's schema and then `Ord for AlgebraicValue`.
504506
///
505507
/// **NOTE:** Do not use directly.
506-
/// This is exposed as `filter_by_{$field_name}` on types with `#[spacetimedb(table)]`.
508+
/// This is exposed as `filter_by_{$field_name}` on types with `#[spacetimedb::table]`.
507509
#[doc(hidden)]
508510
pub fn filter_by_field<Table: TableType, T: FilterableValue, const COL_IDX: u16>(val: &T) -> TableIter<Table> {
509511
let iter = iter_by_col_eq(Table::table_id(), COL_IDX.into(), val).expect("iter_by_col_eq failed");
@@ -517,7 +519,7 @@ pub mod query {
517519
/// Returns the number of deleted rows.
518520
///
519521
/// **NOTE:** Do not use directly.
520-
/// This is exposed as `delete_by_{$field_name}` on types with `#[spacetimedb(table)]`
522+
/// This is exposed as `delete_by_{$field_name}` on types with `#[spacetimedb::table]`
521523
/// where the field does not have a unique constraint.
522524
#[doc(hidden)]
523525
pub fn delete_by_field<Table: TableType, T: FilterableValue, const COL_IDX: u16>(val: &T) -> u32 {
@@ -534,7 +536,7 @@ pub mod query {
534536
/// Returns whether any rows were deleted.
535537
///
536538
/// **NOTE:** Do not use directly.
537-
/// This is exposed as `delete_by_{$field_name}` on types with `#[spacetimedb(table)]`
539+
/// This is exposed as `delete_by_{$field_name}` on types with `#[spacetimedb::table]`
538540
/// where the field has a unique constraint.
539541
pub fn delete_by_unique_field<Table: TableType, T: FilterableValue, const COL_IDX: u16>(val: &T) -> bool {
540542
let count = delete_by_field::<Table, T, COL_IDX>(val);
@@ -548,7 +550,7 @@ pub mod query {
548550
/// according to the column's schema and then `Ord for AlgebraicValue`.
549551
///
550552
/// **NOTE:** Do not use directly.
551-
/// This is exposed as `update_by_{$field_name}` on types with `#[spacetimedb(table)]`.
553+
/// This is exposed as `update_by_{$field_name}` on types with `#[spacetimedb::table]`.
552554
#[doc(hidden)]
553555
pub fn update_by_field<Table: TableType, T: FilterableValue, const COL_IDX: u16>(old: &T, new: Table) -> bool {
554556
// Delete the existing row, if any.

crates/bindings/src/rt.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ impl ReducerArg<'_> for ReducerContext {}
106106
pub fn assert_reducer_arg<'de, T: ReducerArg<'de>>() {}
107107
/// Assert that `T: IntoReducerResult`.
108108
pub fn assert_reducer_ret<T: IntoReducerResult>() {}
109-
/// Assert that `T: TableType`.
110-
pub const fn assert_table<T: TableType>() {}
109+
pub const fn assert_reducer_typecheck<'de, A: Args<'de>, T>(_: impl Reducer<'de, A, T> + Copy) {}
111110

112111
/// Used in the last type parameter of `Reducer` to indicate that the
113112
/// context argument *should* be passed to the reducer logic.

crates/cli/src/subcommands/project/rust/lib._rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1-
use spacetimedb::{spacetimedb, ReducerContext};
1+
use spacetimedb::ReducerContext;
22

3-
#[spacetimedb(table)]
3+
#[spacetimedb::table]
44
pub struct Person {
55
name: String
66
}
77

8-
#[spacetimedb(init)]
8+
#[spacetimedb::reducer(init)]
99
pub fn init() {
1010
// Called when the module is initially published
1111
}
1212

13-
#[spacetimedb(connect)]
13+
#[spacetimedb::reducer(client_connected)]
1414
pub fn identity_connected(_ctx: ReducerContext) {
1515
// Called everytime a new client connects
1616
}
1717

18-
#[spacetimedb(disconnect)]
18+
#[spacetimedb::reducer(client_disconnected)]
1919
pub fn identity_disconnected(_ctx: ReducerContext) {
2020
// Called everytime a client disconnects
2121
}
2222

23-
#[spacetimedb(reducer)]
23+
#[spacetimedb::reducer]
2424
pub fn add(name: String) {
2525
Person::insert(Person { name });
2626
}
2727

28-
#[spacetimedb(reducer)]
28+
#[spacetimedb::reducer]
2929
pub fn say_hello() {
3030
for person in Person::iter() {
3131
log::info!("Hello, {}!", person.name);

crates/core/src/db/datastore/locking_tx_datastore/committed_state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl CommittedState {
262262
/// Compute the system table schemas from the system tables,
263263
/// and store those schemas in the in-memory [`Table`] structures.
264264
///
265-
/// Necessary during bootstrap because system tables include autoinc IDs
265+
/// Necessary during bootstrap because system tables include auto_inc IDs
266266
/// for objects like indexes and constraints
267267
/// which are computed at insert-time,
268268
/// and therefore not included in the hardcoded schemas.

crates/core/src/db/datastore/locking_tx_datastore/datastore.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl Locking {
143143
/// - Populate those tables with all rows in `snapshot`.
144144
/// - Construct a [`HashMapBlobStore`] containing all the large blobs referenced by `snapshot`,
145145
/// with reference counts specified in `snapshot`.
146-
/// - Do [`CommittedState::reset_system_table_schemas`] to fix-up autoinc IDs in the system tables,
146+
/// - Do [`CommittedState::reset_system_table_schemas`] to fix-up auto_inc IDs in the system tables,
147147
/// to ensure those schemas match what [`Self::bootstrap`] would install.
148148
/// - Notably, **do not** construct indexes or sequences.
149149
/// This should be done by [`Self::rebuild_state_after_replay`],
@@ -201,7 +201,7 @@ impl Locking {
201201
.set(table_size as i64);
202202
}
203203

204-
// Fix up autoinc IDs in the cached system table schemas.
204+
// Fix up auto_inc IDs in the cached system table schemas.
205205
committed_state.reset_system_table_schemas(database_address)?;
206206

207207
// The next TX offset after restoring from a snapshot is one greater than the snapshotted offset.
@@ -1901,7 +1901,7 @@ mod tests {
19011901

19021902
// Insert a row and commit the tx.
19031903
let row = u32_str_u32(0, "Foo", 18); // 0 will be ignored.
1904-
// Because of autoinc columns, we will get a slightly different
1904+
// Because of auto_inc columns, we will get a slightly different
19051905
// value than the one we inserted.
19061906
let row = datastore.insert_mut_tx(&mut tx, table_id, row)?.1.to_product_value();
19071907
datastore.commit_mut_tx_for_test(tx)?;
@@ -1922,7 +1922,7 @@ mod tests {
19221922

19231923
// Update the db with the same actual value for that row, in a new tx.
19241924
let mut tx = datastore.begin_mut_tx(IsolationLevel::Serializable);
1925-
// Iterate over all rows with the value 1 (from the autoinc) in column 0.
1925+
// Iterate over all rows with the value 1 (from the auto_inc) in column 0.
19261926
let rows = all_rows_col_0_eq_1(&tx);
19271927
assert_eq!(rows.len(), 1);
19281928
assert_eq!(row, rows[0]);
@@ -1973,7 +1973,7 @@ mod tests {
19731973

19741974
// TODO: Add the following tests
19751975
// - Create index with unique constraint and immediately insert a row that violates the constraint before committing.
1976-
// - Create a tx that inserts 2000 rows with an autoinc column
1977-
// - Create a tx that inserts 2000 rows with an autoinc column and then rolls back
1976+
// - Create a tx that inserts 2000 rows with an auto_inc column
1977+
// - Create a tx that inserts 2000 rows with an auto_inc column and then rolls back
19781978
// - Test creating sequences pre_commit, post_commit, post_rollback
19791979
}

crates/core/src/db/update.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,12 +297,12 @@ fn schema_compat_and_updates_hack(
297297
//
298298
// UNSET = {}
299299
// INDEXED = { indexed }
300-
// AUTO_INC = { autoinc }
300+
// AUTO_INC = { auto_inc }
301301
// UNIQUE = INDEXED | { unique } = { unique, indexed }
302-
// IDENTITY = UNIQUE | AUTO_INC = { unique, indexed, autoinc }
302+
// IDENTITY = UNIQUE | AUTO_INC = { unique, indexed, auto_inc }
303303
// PRIMARY_KEY = UNIQUE | { pk } = { unique, indexed, pk }
304-
// PRIMARY_KEY_AUTO = PRIMARY_KEY | AUTO_INC = { unique, indexed, autoinc, pk }
305-
// PRIMARY_KEY_IDENTITY = PRIMARY_KEY | IDENTITY = = { unique, indexed, autoinc, pk }
304+
// PRIMARY_KEY_AUTO = PRIMARY_KEY | AUTO_INC = { unique, indexed, auto_inc, pk }
305+
// PRIMARY_KEY_IDENTITY = PRIMARY_KEY | IDENTITY = = { unique, indexed, auto_inc, pk }
306306
//
307307
// This entails that all attributes with `indexed`,
308308
// that have something additional,

0 commit comments

Comments
 (0)