Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/bindings-macro/src/sats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,14 @@ pub(crate) fn derive_satstype(ty: &SatsType<'_>) -> TokenStream {
// Assume that the type is `Copy`, as most all-unit enums will be.
let filterable_impl = quote! {
#[automatically_derived]
impl #impl_generics #krate::Private for #name #ty_generics #where_clause {}
#[automatically_derived]
impl #impl_generics #krate::FilterableValue for #name #ty_generics #where_clause {
type Column = #name #ty_generics;
}
#[automatically_derived]
impl #impl_generics #krate::Private for &#name #ty_generics #where_clause {}
#[automatically_derived]
impl #impl_generics #krate::FilterableValue for &#name #ty_generics #where_clause {
type Column = #name #ty_generics;
}
Expand Down
4 changes: 3 additions & 1 deletion crates/bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,8 @@ Any index supports getting a [`RangedIndex`] using [`ctx`](crate::ReducerContext
- [`RangedIndex::filter`]
- [`RangedIndex::delete`]

Only types which implement [`FilterableValue`](trait@crate::FilterableValue) may be used as index keys.

## Reducers

Reducers are declared using the [`#[reducer]` macro](macro@crate::reducer).
Expand Down Expand Up @@ -585,4 +587,4 @@ Currently, manual migration support is limited. The `spacetime publish --clear-d
[clients]: https://spacetimedb.com/docs/#client
[client SDK documentation]: https://spacetimedb.com/docs/#client
[host]: https://spacetimedb.com/docs/#host
[SEQUENCE]: https://spacetimedb.com/docs/appendix#sequence
[SEQUENCE]: https://spacetimedb.com/docs/appendix#sequence
2 changes: 2 additions & 0 deletions crates/bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub use spacetimedb_lib::sats;
pub use spacetimedb_lib::ser::Serialize;
pub use spacetimedb_lib::AlgebraicValue;
pub use spacetimedb_lib::ConnectionId;
// `FilterableValue` re-exported purely for rustdoc.
pub use spacetimedb_lib::FilterableValue;
pub use spacetimedb_lib::Identity;
pub use spacetimedb_lib::ScheduleAt;
pub use spacetimedb_lib::TimeDuration;
Expand Down
4 changes: 2 additions & 2 deletions crates/bindings/tests/ui/tables.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ error[E0277]: `&'a Alpha` cannot appear as an argument to an index filtering ope
--> tests/ui/tables.rs:32:33
|
32 | ctx.db.delta().compound_a().find(Alpha { beta: 0 });
| ^^^^ should be an integer type, `bool`, `String`, `&str`, `Identity`, `ConnectionId`, or `Hash`, not `&'a Alpha`
| ^^^^ should be an integer type, `bool`, `String`, `&str`, `Identity`, `ConnectionId`, `Hash` or a no-payload enum which derives `SpacetimeType`, not `&'a Alpha`
|
= help: the trait `for<'a> FilterableValue` is not implemented for `&'a Alpha`
= note: The allowed set of types are limited to integers, bool, strings, `Identity`, `ConnectionId`, and `Hash`
= note: The allowed set of types are limited to integers, bool, strings, `Identity`, `ConnectionId`, `Hash` and no-payload enums which derive `SpacetimeType`,
= help: the following other types implement trait `FilterableValue`:
&ConnectionId
&Identity
Expand Down
59 changes: 41 additions & 18 deletions crates/lib/src/filterable_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,55 @@ use spacetimedb_sats::{hash::Hash, i256, u256, Serialize};
///
/// Types which can appear specifically as a terminating bound in a BTree index,
/// which may be a range, instead use [`IndexScanRangeBoundsTerminator`].
///
/// General rules for implementors of this type:
/// - It should only be implemented for types that have
/// simple-to-implement consistent total equality and ordering
/// on all languages SpacetimeDB supports in both client and module SDKs.
/// This means that user-defined compound types other than C-style enums,
/// and arrays thereof,
/// should not implement it, as C# and TypeScript use reference equality for those types.
/// - It should only be implemented for owned values if those values are `Copy`.
/// Otherwise it should only be implemented for references.
/// This is so that rustc and IDEs will recommend rewriting `x` to `&x` rather than `x.clone()`.
/// - `Arg: FilterableValue<Column = Col>`
/// for any pair of types `(Arg, Col)` which meet the above criteria
/// is desirable if `Arg` and `Col` have the same BSATN layout.
/// E.g. `&str: FilterableValue<Column = String>` is desirable.
/// Because SpacetimeDB supports a only restricted set of types as index keys,
/// only a small set of `Column` types have corresponding `FilterableValue` implementations.
/// Specifically, these types are:
/// - Signed and unsigned integers of various widths.
/// - [`bool`].
/// - [`String`], which is also filterable with `&str`.
/// - [`Identity`].
/// - [`ConnectionId`].
/// - [`Hash`](struct@Hash).
/// - No-payload enums annotated with `#[derive(SpacetimeType)]`.
/// No-payload enums are sometimes called "plain," "simple" or "C-style."
/// They are enums where no variant has any payload data.
//
// General rules for implementors of this type:
// - It should only be implemented for types that have
// simple-to-implement consistent total equality and ordering
// on all languages SpacetimeDB supports in both client and module SDKs.
// This means that user-defined compound types other than C-style enums,
// and arrays thereof,
// should not implement it, as C# and TypeScript use reference equality for those types.
// - It should only be implemented for owned values if those values are `Copy`.
// Otherwise it should only be implemented for references.
// This is so that rustc and IDEs will recommend rewriting `x` to `&x` rather than `x.clone()`.
// - `Arg: FilterableValue<Column = Col>`
// for any pair of types `(Arg, Col)` which meet the above criteria
// is desirable if `Arg` and `Col` have the same BSATN layout.
// E.g. `&str: FilterableValue<Column = String>` is desirable.
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot appear as an argument to an index filtering operation",
label = "should be an integer type, `bool`, `String`, `&str`, `Identity`, `ConnectionId`, or `Hash`, not `{Self}`",
note = "The allowed set of types are limited to integers, bool, strings, `Identity`, `ConnectionId`, and `Hash`"
label = "should be an integer type, `bool`, `String`, `&str`, `Identity`, `ConnectionId`, `Hash` or a no-payload enum which derives `SpacetimeType`, not `{Self}`",
note = "The allowed set of types are limited to integers, bool, strings, `Identity`, `ConnectionId`, `Hash` and no-payload enums which derive `SpacetimeType`,"
)]
pub trait FilterableValue: Serialize {
pub trait FilterableValue: Serialize + Private {
type Column;
}

/// Hidden supertrait for [`FilterableValue`],
/// to discourage users from hand-writing implementations.
///
/// We want to expose [`FilterableValue`] in the docs, but to prevent users from implementing it.
/// Normally, we would just make this `Private` trait inaccessible,
/// but we need to macro-generate implementations, so it must be `pub`.
/// We mark it `doc(hidden)` to discourage use.
#[doc(hidden)]
pub trait Private {}

macro_rules! impl_filterable_value {
(@one $arg:ty => $col:ty) => {
impl Private for $arg {}
impl FilterableValue for $arg {
type Column = $col;
}
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub mod type_value {

pub use connection_id::ConnectionId;
pub use direct_index_key::{assert_column_type_valid_for_direct_index, DirectIndexKey};
#[doc(hidden)]
pub use filterable_value::Private;
pub use filterable_value::{FilterableValue, IndexScanRangeBoundsTerminator, TermBound};
pub use identity::Identity;
pub use scheduler::ScheduleAt;
Expand Down
Loading