use std::any::{Any, TypeId, type_name};
use std::fmt;
use std::iter::Copied;
use std::marker::PhantomData;
use std::{mem, slice};
use pragma_core::graph::{self, TaskContext};
use crate::access::{Access, AccessKind};
use crate::change::{Mut, MutIter, MutSlice};
use crate::component::{ColumnData, Ticks};
use crate::lend::Columns;
use crate::{Component, EcsError, Entity};
mod sealed {
pub trait Sealed {}
}
pub(crate) use sealed::Sealed;
#[doc(hidden)]
#[derive(Debug)]
pub enum ColumnRef<'w> {
Unique(&'w mut dyn Any),
Shared(&'w dyn Any),
}
fn take_shared<'w, T: Component>(column: &mut Option<ColumnRef<'w>>) -> Option<&'w [T]> {
let shared: &'w dyn Any = match column.take()? {
ColumnRef::Unique(c) => c,
ColumnRef::Shared(c) => c,
};
*column = Some(ColumnRef::Shared(shared));
shared
.downcast_ref::<ColumnData<T>>()
.map(ColumnData::values)
}
fn take_unique<'w, T: Component>(
column: &mut Option<ColumnRef<'w>>,
) -> Option<(&'w mut [T], &'w mut [u64])> {
match column.take()? {
ColumnRef::Unique(c) => c.downcast_mut::<ColumnData<T>>().map(ColumnData::parts_mut),
shared @ ColumnRef::Shared(_) => {
*column = Some(shared);
None
}
}
}
pub trait QueryData: Sealed {
type Item<'w>;
#[doc(hidden)]
type State<'w>: Default;
#[doc(hidden)]
type Slice<'w>: Send;
#[doc(hidden)]
type Iter<'w>: Iterator<Item = Self::Item<'w>>;
#[doc(hidden)]
fn declare(access: &mut Access) -> Result<(), EcsError>;
#[doc(hidden)]
fn holds(ty: TypeId) -> bool;
#[doc(hidden)]
fn matches(types: &[TypeId]) -> bool;
#[doc(hidden)]
fn offer<'w>(state: &mut Self::State<'w>, ty: TypeId, column: &mut Option<ColumnRef<'w>>);
#[doc(hidden)]
fn finish<'w>(
state: Self::State<'w>,
entities: &'w [Entity],
tick: u64,
) -> Option<Self::Slice<'w>>;
#[doc(hidden)]
fn split_at<'w>(slice: Self::Slice<'w>, mid: usize) -> (Self::Slice<'w>, Self::Slice<'w>);
#[doc(hidden)]
fn iter<'w>(slice: Self::Slice<'w>) -> Self::Iter<'w>;
#[doc(hidden)]
type Guard<'w>;
#[doc(hidden)]
type Cursor<'a>;
#[doc(hidden)]
fn acquire<'w>(ctx: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError>;
#[doc(hidden)]
fn lent_ticks<'a>(guard: &'a Self::Guard<'_>, ty: TypeId, table: usize) -> Option<Ticks<'a>>;
#[doc(hidden)]
fn cursor<'a>(guard: &'a mut Self::Guard<'_>) -> Self::Cursor<'a>;
#[doc(hidden)]
fn lent<'a>(
cursor: &mut Self::Cursor<'a>,
id: usize,
entities: &'a [Entity],
tick: u64,
) -> Option<Self::Slice<'a>>;
}
fn lent_shared<T>(columns: &[ColumnData<T>], id: usize, rows: usize) -> Option<&[T]> {
columns
.get(id)
.map(ColumnData::values)
.filter(|column| column.len() == rows)
}
fn lent_ticks_of<T: Component>(
columns: &Columns<T>,
ty: TypeId,
table: usize,
) -> Option<Ticks<'_>> {
if ty == TypeId::of::<T>() {
columns.tables().get(table).map(ColumnData::ticks)
} else {
None
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct MutCursor<'a, T> {
rest: &'a mut [ColumnData<T>],
offset: usize,
}
pub trait ReadOnlyQueryData: QueryData {}
pub trait QueryFilter: Sealed {
#[doc(hidden)]
const DENSE: bool;
#[doc(hidden)]
fn matches(types: &[TypeId]) -> bool;
#[doc(hidden)]
fn declare(access: &mut Access) -> Result<(), EcsError>;
#[doc(hidden)]
fn mask<'a>(ticks: &dyn Fn(TypeId) -> Option<Ticks<'a>>, last_run: u64, mask: &mut [bool]);
#[doc(hidden)]
type Guard<'w>;
#[doc(hidden)]
fn acquire<'w, Q: QueryData>(ctx: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError>;
#[doc(hidden)]
fn lent_ticks<'a>(guard: &'a Self::Guard<'_>, ty: TypeId, table: usize) -> Option<Ticks<'a>>;
}
fn has<T: Component>(types: &[TypeId]) -> bool {
types.binary_search(&TypeId::of::<T>()).is_ok()
}
impl<T: Component> Sealed for &T {}
impl<T: Component> QueryData for &T {
type Item<'w> = &'w T;
type State<'w> = Option<&'w [T]>;
type Slice<'w> = &'w [T];
type Iter<'w> = slice::Iter<'w, T>;
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_component::<T>(AccessKind::Read)
}
fn holds(ty: TypeId) -> bool {
ty == TypeId::of::<T>()
}
fn matches(types: &[TypeId]) -> bool {
has::<T>(types)
}
fn offer<'w>(state: &mut Self::State<'w>, ty: TypeId, column: &mut Option<ColumnRef<'w>>) {
if ty == TypeId::of::<T>() && state.is_none() {
*state = take_shared(column);
}
}
fn finish<'w>(
state: Self::State<'w>,
entities: &'w [Entity],
_: u64,
) -> Option<Self::Slice<'w>> {
debug_assert!(state.is_none_or(|s| s.len() == entities.len()));
state
}
fn split_at<'w>(slice: Self::Slice<'w>, mid: usize) -> (Self::Slice<'w>, Self::Slice<'w>) {
slice.split_at(mid.min(slice.len()))
}
fn iter<'w>(slice: Self::Slice<'w>) -> Self::Iter<'w> {
slice.iter()
}
type Guard<'w> = graph::Res<'w, Columns<T>>;
type Cursor<'a> = &'a [ColumnData<T>];
fn acquire<'w>(ctx: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError> {
ctx.read::<Columns<T>>().map_err(EcsError::graph)
}
fn lent_ticks<'a>(guard: &'a Self::Guard<'_>, ty: TypeId, table: usize) -> Option<Ticks<'a>> {
lent_ticks_of(guard, ty, table)
}
fn cursor<'a>(guard: &'a mut Self::Guard<'_>) -> Self::Cursor<'a> {
guard.tables()
}
fn lent<'a>(
cursor: &mut Self::Cursor<'a>,
id: usize,
entities: &'a [Entity],
_: u64,
) -> Option<Self::Slice<'a>> {
lent_shared(cursor, id, entities.len())
}
}
impl<T: Component> ReadOnlyQueryData for &T {}
impl<T: Component> Sealed for &mut T {}
impl<T: Component> QueryData for &mut T {
type Item<'w> = Mut<'w, T>;
type State<'w> = Option<(&'w mut [T], &'w mut [u64])>;
type Slice<'w> = MutSlice<'w, T>;
type Iter<'w> = MutIter<'w, T>;
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_component::<T>(AccessKind::Write)
}
fn holds(ty: TypeId) -> bool {
ty == TypeId::of::<T>()
}
fn matches(types: &[TypeId]) -> bool {
has::<T>(types)
}
fn offer<'w>(state: &mut Self::State<'w>, ty: TypeId, column: &mut Option<ColumnRef<'w>>) {
if ty == TypeId::of::<T>() && state.is_none() {
*state = take_unique(column);
}
}
fn finish<'w>(
state: Self::State<'w>,
entities: &'w [Entity],
tick: u64,
) -> Option<Self::Slice<'w>> {
let (values, ticks) = state?;
debug_assert_eq!(values.len(), entities.len());
MutSlice::new(values, ticks, tick)
}
fn split_at<'w>(slice: Self::Slice<'w>, mid: usize) -> (Self::Slice<'w>, Self::Slice<'w>) {
slice.split_at(mid)
}
fn iter<'w>(slice: Self::Slice<'w>) -> Self::Iter<'w> {
slice.into_iter()
}
type Guard<'w> = graph::ResMut<'w, Columns<T>>;
type Cursor<'a> = MutCursor<'a, T>;
fn acquire<'w>(ctx: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError> {
ctx.write::<Columns<T>>().map_err(EcsError::graph)
}
fn lent_ticks<'a>(guard: &'a Self::Guard<'_>, ty: TypeId, table: usize) -> Option<Ticks<'a>> {
lent_ticks_of(guard, ty, table)
}
fn cursor<'a>(guard: &'a mut Self::Guard<'_>) -> Self::Cursor<'a> {
MutCursor {
rest: guard.tables_mut(),
offset: 0,
}
}
fn lent<'a>(
cursor: &mut Self::Cursor<'a>,
id: usize,
entities: &'a [Entity],
tick: u64,
) -> Option<Self::Slice<'a>> {
let rest = mem::take(&mut cursor.rest);
let skip = id.checked_sub(cursor.offset)?.min(rest.len());
let (column, rest) = rest.split_at_mut(skip).1.split_first_mut()?;
cursor.rest = rest;
cursor.offset = id.saturating_add(1);
if column.len() != entities.len() {
return None;
}
let (values, ticks) = column.parts_mut();
MutSlice::new(values, ticks, tick)
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct OptionSlice<'w, T> {
values: Option<&'w [T]>,
len: usize,
}
#[doc(hidden)]
#[derive(Debug)]
pub struct OptionIter<'w, T> {
values: Option<slice::Iter<'w, T>>,
remaining: usize,
}
impl<'w, T> Iterator for OptionIter<'w, T> {
type Item = Option<&'w T>;
fn next(&mut self) -> Option<Self::Item> {
self.remaining = self.remaining.checked_sub(1)?;
Some(self.values.as_mut().and_then(Iterator::next))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<T: Component> Sealed for Option<&T> {}
impl<T: Component> QueryData for Option<&T> {
type Item<'w> = Option<&'w T>;
type State<'w> = Option<&'w [T]>;
type Slice<'w> = OptionSlice<'w, T>;
type Iter<'w> = OptionIter<'w, T>;
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_component::<T>(AccessKind::Read)
}
fn holds(ty: TypeId) -> bool {
ty == TypeId::of::<T>()
}
fn matches(_: &[TypeId]) -> bool {
true
}
fn offer<'w>(state: &mut Self::State<'w>, ty: TypeId, column: &mut Option<ColumnRef<'w>>) {
<&T>::offer(state, ty, column);
}
fn finish<'w>(
state: Self::State<'w>,
entities: &'w [Entity],
_: u64,
) -> Option<Self::Slice<'w>> {
debug_assert!(state.is_none_or(|s| s.len() == entities.len()));
Some(OptionSlice {
values: state,
len: entities.len(),
})
}
fn split_at<'w>(slice: Self::Slice<'w>, mid: usize) -> (Self::Slice<'w>, Self::Slice<'w>) {
let mid = mid.min(slice.len);
let rest = slice.len.saturating_sub(mid);
let (left, right) = match slice.values {
Some(values) => {
let (left, right) = values.split_at(mid.min(values.len()));
(Some(left), Some(right))
}
None => (None, None),
};
(
OptionSlice {
values: left,
len: mid,
},
OptionSlice {
values: right,
len: rest,
},
)
}
fn iter<'w>(slice: Self::Slice<'w>) -> Self::Iter<'w> {
OptionIter {
values: slice.values.map(<[T]>::iter),
remaining: slice.len,
}
}
type Guard<'w> = graph::Res<'w, Columns<T>>;
type Cursor<'a> = &'a [ColumnData<T>];
fn acquire<'w>(ctx: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError> {
<&T>::acquire(ctx)
}
fn lent_ticks<'a>(guard: &'a Self::Guard<'_>, ty: TypeId, table: usize) -> Option<Ticks<'a>> {
lent_ticks_of(guard, ty, table)
}
fn cursor<'a>(guard: &'a mut Self::Guard<'_>) -> Self::Cursor<'a> {
guard.tables()
}
fn lent<'a>(
cursor: &mut Self::Cursor<'a>,
id: usize,
entities: &'a [Entity],
_: u64,
) -> Option<Self::Slice<'a>> {
Some(OptionSlice {
values: lent_shared(cursor, id, entities.len()),
len: entities.len(),
})
}
}
impl<T: Component> ReadOnlyQueryData for Option<&T> {}
impl Sealed for Entity {}
impl QueryData for Entity {
type Item<'w> = Entity;
type State<'w> = ();
type Slice<'w> = &'w [Entity];
type Iter<'w> = Copied<slice::Iter<'w, Entity>>;
fn declare(_: &mut Access) -> Result<(), EcsError> {
Ok(())
}
fn holds(_: TypeId) -> bool {
false
}
fn matches(_: &[TypeId]) -> bool {
true
}
fn offer<'w>(_: &mut Self::State<'w>, _: TypeId, _: &mut Option<ColumnRef<'w>>) {}
fn finish<'w>((): Self::State<'w>, entities: &'w [Entity], _: u64) -> Option<Self::Slice<'w>> {
Some(entities)
}
fn split_at<'w>(slice: Self::Slice<'w>, mid: usize) -> (Self::Slice<'w>, Self::Slice<'w>) {
slice.split_at(mid.min(slice.len()))
}
fn iter<'w>(slice: Self::Slice<'w>) -> Self::Iter<'w> {
slice.iter().copied()
}
type Guard<'w> = ();
type Cursor<'a> = ();
fn acquire<'w>(_: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError> {
Ok(())
}
fn lent_ticks<'a>((): &'a Self::Guard<'_>, _: TypeId, _: usize) -> Option<Ticks<'a>> {
None
}
fn cursor<'a>((): &'a mut Self::Guard<'_>) -> Self::Cursor<'a> {}
fn lent<'a>(
(): &mut Self::Cursor<'a>,
_: usize,
entities: &'a [Entity],
_: u64,
) -> Option<Self::Slice<'a>> {
Some(entities)
}
}
impl ReadOnlyQueryData for Entity {}
#[doc(hidden)]
#[derive(Debug)]
pub struct TupleIter<I>(I);
macro_rules! tuple_query {
($($name:ident $index:tt),+) => {
impl<$($name),+> Sealed for ($($name,)+) {}
impl<$($name: QueryData),+> QueryData for ($($name,)+) {
type Item<'w> = ($($name::Item<'w>,)+);
type State<'w> = ($($name::State<'w>,)+);
type Slice<'w> = ($($name::Slice<'w>,)+);
type Iter<'w> = TupleIter<($($name::Iter<'w>,)+)>;
fn declare(access: &mut Access) -> Result<(), EcsError> {
$($name::declare(access)?;)+
Ok(())
}
fn holds(ty: TypeId) -> bool {
$($name::holds(ty))||+
}
fn matches(types: &[TypeId]) -> bool {
$($name::matches(types))&&+
}
fn offer<'w>(
state: &mut Self::State<'w>,
ty: TypeId,
column: &mut Option<ColumnRef<'w>>,
) {
$($name::offer(&mut state.$index, ty, column);)+
}
fn finish<'w>(
state: Self::State<'w>,
entities: &'w [Entity],
tick: u64,
) -> Option<Self::Slice<'w>> {
Some(($($name::finish(state.$index, entities, tick)?,)+))
}
fn split_at<'w>(
slice: Self::Slice<'w>,
mid: usize,
) -> (Self::Slice<'w>, Self::Slice<'w>) {
let halves = ($($name::split_at(slice.$index, mid),)+);
(($(halves.$index.0,)+), ($(halves.$index.1,)+))
}
fn iter<'w>(slice: Self::Slice<'w>) -> Self::Iter<'w> {
TupleIter(($($name::iter(slice.$index),)+))
}
type Guard<'w> = ($($name::Guard<'w>,)+);
type Cursor<'a> = ($($name::Cursor<'a>,)+);
fn acquire<'w>(ctx: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError> {
Ok(($($name::acquire(ctx)?,)+))
}
fn lent_ticks<'a>(
guard: &'a Self::Guard<'_>,
ty: TypeId,
table: usize,
) -> Option<Ticks<'a>> {
None$(.or_else(|| $name::lent_ticks(&guard.$index, ty, table)))+
}
fn cursor<'a>(guard: &'a mut Self::Guard<'_>) -> Self::Cursor<'a> {
($($name::cursor(&mut guard.$index),)+)
}
fn lent<'a>(
cursor: &mut Self::Cursor<'a>,
id: usize,
entities: &'a [Entity],
tick: u64,
) -> Option<Self::Slice<'a>> {
Some(($($name::lent(&mut cursor.$index, id, entities, tick)?,)+))
}
}
impl<$($name: ReadOnlyQueryData),+> ReadOnlyQueryData for ($($name,)+) {}
impl<$($name: Iterator),+> Iterator for TupleIter<($($name,)+)> {
type Item = ($($name::Item,)+);
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
Some(($(self.0.$index.next()?,)+))
}
}
impl<$($name: QueryFilter),+> QueryFilter for ($($name,)+) {
const DENSE: bool = $($name::DENSE)&&+;
fn matches(types: &[TypeId]) -> bool {
$($name::matches(types))&&+
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
$($name::declare(access)?;)+
Ok(())
}
fn mask<'a>(
ticks: &dyn Fn(TypeId) -> Option<Ticks<'a>>,
last_run: u64,
mask: &mut [bool],
) {
$($name::mask(ticks, last_run, mask);)+
}
type Guard<'w> = ($($name::Guard<'w>,)+);
fn acquire<'w, Q: QueryData>(
ctx: &TaskContext<'w>,
) -> Result<Self::Guard<'w>, EcsError> {
Ok(($($name::acquire::<Q>(ctx)?,)+))
}
fn lent_ticks<'a>(
guard: &'a Self::Guard<'_>,
ty: TypeId,
table: usize,
) -> Option<Ticks<'a>> {
None$(.or_else(|| $name::lent_ticks(&guard.$index, ty, table)))+
}
}
};
}
tuple_query!(A 0);
tuple_query!(A 0, B 1);
tuple_query!(A 0, B 1, C 2);
tuple_query!(A 0, B 1, C 2, D 3);
tuple_query!(A 0, B 1, C 2, D 3, E 4);
tuple_query!(A 0, B 1, C 2, D 3, E 4, F 5);
tuple_query!(A 0, B 1, C 2, D 3, E 4, F 5, G 6);
tuple_query!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7);
impl Sealed for () {}
macro_rules! table_filter {
() => {
const DENSE: bool = true;
fn declare(_: &mut Access) -> Result<(), EcsError> {
Ok(())
}
fn mask<'a>(_: &dyn Fn(TypeId) -> Option<Ticks<'a>>, _: u64, _: &mut [bool]) {}
type Guard<'w> = ();
fn acquire<'w, Q: QueryData>(_: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError> {
Ok(())
}
fn lent_ticks<'a>((): &'a Self::Guard<'_>, _: TypeId, _: usize) -> Option<Ticks<'a>> {
None
}
};
}
impl QueryFilter for () {
table_filter!();
fn matches(_: &[TypeId]) -> bool {
true
}
}
pub struct With<T: Component>(PhantomData<fn() -> T>);
impl<T: Component> fmt::Debug for With<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "With<{}>", type_name::<T>())
}
}
impl<T: Component> Sealed for With<T> {}
impl<T: Component> QueryFilter for With<T> {
table_filter!();
fn matches(types: &[TypeId]) -> bool {
has::<T>(types)
}
}
pub struct Without<T: Component>(PhantomData<fn() -> T>);
impl<T: Component> fmt::Debug for Without<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Without<{}>", type_name::<T>())
}
}
impl<T: Component> Sealed for Without<T> {}
impl<T: Component> QueryFilter for Without<T> {
table_filter!();
fn matches(types: &[TypeId]) -> bool {
!has::<T>(types)
}
}
macro_rules! tick_filter {
($which:ident) => {
const DENSE: bool = false;
fn matches(types: &[TypeId]) -> bool {
has::<T>(types)
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_filter_read::<T>()
}
fn mask<'a>(ticks: &dyn Fn(TypeId) -> Option<Ticks<'a>>, last_run: u64, mask: &mut [bool]) {
match ticks(TypeId::of::<T>()) {
Some(ticks) if ticks.$which.len() == mask.len() => {
for (keep, tick) in mask.iter_mut().zip(ticks.$which) {
*keep &= *tick > last_run;
}
}
_ => mask.fill(false),
}
}
type Guard<'w> = Option<graph::Res<'w, Columns<T>>>;
fn acquire<'w, Q: QueryData>(ctx: &TaskContext<'w>) -> Result<Self::Guard<'w>, EcsError> {
if Q::holds(TypeId::of::<T>()) {
Ok(None)
} else {
ctx.read::<Columns<T>>().map(Some).map_err(EcsError::graph)
}
}
fn lent_ticks<'a>(
guard: &'a Self::Guard<'_>,
ty: TypeId,
table: usize,
) -> Option<Ticks<'a>> {
lent_ticks_of(guard.as_ref()?, ty, table)
}
};
}
pub struct Added<T: Component>(PhantomData<fn() -> T>);
impl<T: Component> fmt::Debug for Added<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Added<{}>", type_name::<T>())
}
}
impl<T: Component> Sealed for Added<T> {}
impl<T: Component> QueryFilter for Added<T> {
tick_filter!(added);
}
pub struct Changed<T: Component>(PhantomData<fn() -> T>);
impl<T: Component> fmt::Debug for Changed<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Changed<{}>", type_name::<T>())
}
}
impl<T: Component> Sealed for Changed<T> {}
impl<T: Component> QueryFilter for Changed<T> {
tick_filter!(changed);
}
#[cfg(test)]
mod tests {
use super::*;
fn column(values: &[u32]) -> ColumnData<u32> {
let mut column = ColumnData::default();
for (tick, value) in (1..).zip(values) {
column.push(*value, tick, tick);
}
column
}
#[test]
fn a_shared_column_is_not_handed_out_for_writing() {
let mut values = column(&[1, 2]);
let mut column = Some(ColumnRef::Unique(&mut values));
assert_eq!(take_shared::<u32>(&mut column), Some(&[1, 2][..]));
assert_eq!(take_shared::<u32>(&mut column), Some(&[1, 2][..]));
assert!(take_unique::<u32>(&mut column).is_none());
assert!(matches!(column, Some(ColumnRef::Shared(_))));
assert!(take_shared::<u64>(&mut column).is_none());
}
#[test]
fn option_slices_split_with_and_without_a_column() {
let values = [1, 2, 3];
let present = OptionSlice {
values: Some(&values[..]),
len: 3,
};
let (left, right) = <Option<&i32> as QueryData>::split_at(present, 2);
let left: Vec<_> = <Option<&i32> as QueryData>::iter(left).collect();
let right: Vec<_> = <Option<&i32> as QueryData>::iter(right).collect();
assert_eq!(left, [Some(&1), Some(&2)]);
assert_eq!(right, [Some(&3)]);
let absent = OptionSlice::<i32> {
values: None,
len: 3,
};
let (left, right) = <Option<&i32> as QueryData>::split_at(absent, 5);
assert_eq!(<Option<&i32> as QueryData>::iter(left).count(), 3);
assert_eq!(
<Option<&i32> as QueryData>::iter(right).size_hint(),
(0, Some(0))
);
}
#[test]
fn tick_filters_clear_failing_rows() {
let ticks = column(&[7, 8, 9]);
let lookup = |ty: TypeId| (ty == TypeId::of::<u32>()).then(|| ticks.ticks());
let mut mask = [true, true, false];
<Changed<u32> as QueryFilter>::mask(&lookup, 1, &mut mask);
assert_eq!(mask, [false, true, false]);
let mut mask = [true; 3];
<(Added<u32>, With<u8>) as QueryFilter>::mask(&lookup, 2, &mut mask);
assert_eq!(mask, [false, false, true]);
let mut mask = [true; 3];
<Changed<u64> as QueryFilter>::mask(&lookup, 0, &mut mask);
assert_eq!(mask, [false; 3]);
const { assert!(<(With<u8>, Without<u16>) as QueryFilter>::DENSE) };
const { assert!(!<(With<u8>, Changed<u16>) as QueryFilter>::DENSE) };
assert_eq!(format!("{:?}", Added::<u8>(PhantomData)), "Added<u8>");
}
}