use crate::{ConnectionId, Identity, Timestamp, Uuid};
use core::ops;
use spacetimedb_sats::bsatn;
use spacetimedb_sats::{hash::Hash, i256, u256, Serialize};
#[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`, `Uuid`, `Timestamp`, `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`, `Uuid`, `Timestamp`, `ConnectionId`, `Hash` and no-payload enums which derive `SpacetimeType`,"
)]
pub trait FilterableValue: Serialize + Private {
type Column;
}
#[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;
}
};
(@one $arg:ty: Copy) => {
impl_filterable_value!(@one $arg => $arg);
impl_filterable_value!(@one &$arg => $arg);
};
(@one $arg:ty) => {
impl_filterable_value!(@one &$arg => $arg);
};
($($arg:ty $(: $copy:ident)? $(=> $col:ty)?),* $(,)?) => {
$(impl_filterable_value!(@one $arg $(: $copy)? $(=> $col)?);)*
};
}
impl_filterable_value! {
u8: Copy,
u16: Copy,
u32: Copy,
u64: Copy,
u128: Copy,
u256: Copy,
i8: Copy,
i16: Copy,
i32: Copy,
i64: Copy,
i128: Copy,
i256: Copy,
bool: Copy,
String,
&str => String,
Identity: Copy,
Uuid: Copy,
Timestamp: Copy,
ConnectionId: Copy,
Hash: Copy,
}
pub enum TermBound<T> {
Single(ops::Bound<T>),
Range(ops::Bound<T>, ops::Bound<T>),
}
impl<Bound: FilterableValue> TermBound<&Bound> {
#[inline]
pub fn serialize_into(&self, buf: &mut Vec<u8>) -> Option<usize> {
let (start, end) = match self {
TermBound::Single(elem) => (elem, None),
TermBound::Range(start, end) => (start, Some(end)),
};
bsatn::to_writer(buf, start).unwrap();
end.map(|end| {
let rend_idx = buf.len();
bsatn::to_writer(buf, end).unwrap();
rend_idx
})
}
}
pub trait IndexScanRangeBoundsTerminator {
const POINT: bool = false;
type Arg;
fn point(&self) -> &Self::Arg {
unimplemented!()
}
fn bounds(&self) -> TermBound<&Self::Arg>;
}
impl<Col, Arg: FilterableValue<Column = Col>> IndexScanRangeBoundsTerminator for Arg {
const POINT: bool = true;
type Arg = Arg;
fn point(&self) -> &Arg {
self
}
fn bounds(&self) -> TermBound<&Arg> {
TermBound::Single(ops::Bound::Included(self))
}
}
macro_rules! impl_terminator {
($($range:ty),* $(,)?) => {
$(impl<T: FilterableValue> IndexScanRangeBoundsTerminator for $range {
type Arg = T;
fn bounds(&self) -> TermBound<&T> {
TermBound::Range(
ops::RangeBounds::start_bound(self),
ops::RangeBounds::end_bound(self),
)
}
})*
};
}
impl_terminator!(
ops::Range<T>,
ops::RangeFrom<T>,
ops::RangeInclusive<T>,
ops::RangeTo<T>,
ops::RangeToInclusive<T>,
(ops::Bound<T>, ops::Bound<T>),
);