Experiments in applying Entity-Component-System patterns to durable data storage APIs.
at main 468 lines 13 kB view raw
1use tracing::trace; 2 3use crate::{Entity, EntityId, component::Bundle}; 4 5use super::Component; 6use std::marker::PhantomData; 7 8pub(crate) mod ir; 9 10pub trait QueryData { 11 type Output<'a>: Sized; 12 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>>; 13 fn filter_expression() -> ir::FilterExpression; 14} 15 16pub trait QueryFilter { 17 fn filter_expression() -> ir::FilterExpression; 18} 19 20/// Matches if any of the Filters in `C` matches 21pub struct AnyOf<C>(PhantomData<C>); 22 23/// Matches if Entity has all components in `C` 24pub struct With<C>(PhantomData<C>); 25 26/// Matches if Entity has none of the components in `C` 27pub struct Without<C>(PhantomData<C>); 28 29/// Matches if any of the filters in `F` match 30pub struct Or<F>(F); 31 32pub trait QueryFilterValue: Sized { 33 fn filter_expression(&self) -> ir::FilterExpression; 34} 35 36pub struct Query<'a, D = Entity<'a>, F = (), V = ()> 37where 38 F: ?Sized, 39{ 40 pub(crate) ecs: &'a crate::Ecs, 41 pub(crate) data: PhantomData<D>, 42 pub(crate) filter: PhantomData<F>, 43 pub(crate) filter_value: V, 44} 45 46impl<'a, C, F> Query<'a, C, F, ()> { 47 pub fn new(ecs: &'a crate::Ecs) -> Self { 48 Self { 49 ecs, 50 data: PhantomData, 51 filter: PhantomData, 52 filter_value: (), 53 } 54 } 55} 56 57impl<'a, C, F, V> Query<'a, C, F, V> { 58 pub fn with_filter(ecs: &'a crate::Ecs, filter_value: V) -> Self { 59 Self { 60 ecs, 61 data: PhantomData, 62 filter: PhantomData, 63 filter_value, 64 } 65 } 66} 67 68impl<'a, D, F, V> Query<'a, D, F, V> 69where 70 D: QueryData + 'a, 71 F: QueryFilter, 72 V: QueryFilterValue, 73{ 74 pub fn iter(&self) -> impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V> { 75 self.try_iter().unwrap() 76 } 77 78 pub fn reverse_iter(&self) -> impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V> { 79 self.try_reverse_iter().unwrap() 80 } 81 82 pub fn entities(&self) -> impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V> { 83 self.try_entities().unwrap() 84 } 85 86 pub fn reverse_entities(&self) -> impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V> { 87 self.try_reverse_entities().unwrap() 88 } 89 90 pub fn try_iter( 91 &self, 92 ) -> Result<impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V>, crate::Error> { 93 Ok(self.try_entities()?.filter_map(|e| D::from_entity(e))) 94 } 95 96 pub fn try_reverse_iter( 97 &self, 98 ) -> Result<impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V>, crate::Error> { 99 Ok(self 100 .try_reverse_entities()? 101 .filter_map(|e| D::from_entity(e))) 102 } 103 104 pub fn try_entities( 105 &self, 106 ) -> Result<impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V>, crate::Error> { 107 let mut query = self.as_sql_query(); 108 109 query.order_by = ir::OrderBy::Asc; 110 self.ecs.fetch::<Entity>(query) 111 } 112 113 pub fn try_reverse_entities( 114 &self, 115 ) -> Result<impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V>, crate::Error> { 116 let mut query = self.as_sql_query(); 117 query.order_by = ir::OrderBy::Desc; 118 self.ecs.fetch::<Entity>(query) 119 } 120 121 #[tracing::instrument(level = "debug", skip_all)] 122 fn as_sql_query(&self) -> ir::Query { 123 let filter = ir::FilterExpression::and([ 124 D::filter_expression(), 125 F::filter_expression(), 126 self.filter_value.filter_expression(), 127 ]); 128 129 trace!(?filter); 130 131 ir::Query { 132 filter, 133 order_by: ir::OrderBy::Asc, 134 } 135 } 136} 137 138impl QueryData for () { 139 type Output<'a> = (); 140 141 fn from_entity<'a>(_e: Entity<'a>) -> Option<Self::Output<'a>> { 142 Some(()) 143 } 144 145 fn filter_expression() -> ir::FilterExpression { 146 ir::FilterExpression::none() 147 } 148} 149 150impl QueryData for Entity<'_> { 151 type Output<'a> = Entity<'a>; 152 153 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> { 154 Some(e) 155 } 156 157 fn filter_expression() -> ir::FilterExpression { 158 ir::FilterExpression::none() 159 } 160} 161 162impl QueryData for EntityId { 163 type Output<'a> = EntityId; 164 165 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> { 166 Some(e.id()) 167 } 168 169 fn filter_expression() -> ir::FilterExpression { 170 ir::FilterExpression::none() 171 } 172} 173 174impl<C: Component> QueryData for C { 175 type Output<'a> = C; 176 177 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> { 178 e.component::<C>() 179 } 180 181 fn filter_expression() -> ir::FilterExpression { 182 ir::FilterExpression::with_component(C::component_name()) 183 } 184} 185 186impl QueryFilter for () { 187 fn filter_expression() -> ir::FilterExpression { 188 ir::FilterExpression::none() 189 } 190} 191 192impl<C: Bundle> Default for AnyOf<C> { 193 fn default() -> Self { 194 Self(PhantomData) 195 } 196} 197 198impl<C: Bundle> Default for With<C> { 199 fn default() -> Self { 200 Self(PhantomData) 201 } 202} 203 204impl<C: Bundle> Default for Without<C> { 205 fn default() -> Self { 206 Self(PhantomData) 207 } 208} 209 210impl<F: QueryFilter + Default> Default for Or<F> { 211 fn default() -> Self { 212 Self(F::default()) 213 } 214} 215 216impl<C: Component> QueryFilter for C { 217 fn filter_expression() -> ir::FilterExpression { 218 ir::FilterExpression::with_component(C::component_name()) 219 } 220} 221 222impl<C: Bundle> QueryFilter for AnyOf<C> { 223 fn filter_expression() -> ir::FilterExpression { 224 ir::FilterExpression::or( 225 C::component_names() 226 .iter() 227 .map(|c| ir::FilterExpression::with_component(c)), 228 ) 229 } 230} 231 232impl<C: Component> QueryFilter for With<C> { 233 fn filter_expression() -> ir::FilterExpression { 234 ir::FilterExpression::with_component(C::component_name()) 235 } 236} 237 238impl<C: Component> QueryFilter for Without<C> { 239 fn filter_expression() -> ir::FilterExpression { 240 ir::FilterExpression::without_component(C::component_name()) 241 } 242} 243 244impl QueryFilterValue for () { 245 fn filter_expression(&self) -> ir::FilterExpression { 246 ir::FilterExpression::None 247 } 248} 249 250impl QueryFilterValue for EntityId { 251 fn filter_expression(&self) -> ir::FilterExpression { 252 ir::FilterExpression::entity(*self) 253 } 254} 255 256#[derive(PartialEq, Eq, Debug)] 257pub struct ComponentName(pub String); 258 259impl QueryFilterValue for ComponentName { 260 fn filter_expression(&self) -> ir::FilterExpression { 261 ir::FilterExpression::WithComponent(self.0.clone()) 262 } 263} 264 265impl<V: QueryFilterValue> QueryFilterValue for &[V] { 266 fn filter_expression(&self) -> ir::FilterExpression { 267 ir::FilterExpression::And(self.iter().map(V::filter_expression).collect()) 268 } 269} 270 271impl<C: Component> QueryFilterValue for C { 272 fn filter_expression(&self) -> ir::FilterExpression { 273 use rusqlite::types::ToSqlOutput; 274 275 let value = match C::to_rusqlite(self).unwrap() { 276 ToSqlOutput::Borrowed(v) => v.to_owned().into(), 277 ToSqlOutput::Owned(v) => v, 278 other => unreachable!("{other:?}"), 279 }; 280 281 ir::FilterExpression::with_component_data(C::component_name(), value) 282 } 283} 284 285impl<C: QueryFilterValue + Component> QueryFilterValue for std::ops::Range<C> { 286 fn filter_expression(&self) -> ir::FilterExpression { 287 use rusqlite::types::ToSqlOutput; 288 289 let start = match C::to_rusqlite(&self.start).unwrap() { 290 ToSqlOutput::Borrowed(v) => v.to_owned().into(), 291 ToSqlOutput::Owned(v) => v, 292 other => unreachable!("{other:?}"), 293 }; 294 295 let end = match C::to_rusqlite(&self.end).unwrap() { 296 ToSqlOutput::Borrowed(v) => v.to_owned().into(), 297 ToSqlOutput::Owned(v) => v, 298 other => unreachable!("{other:?}"), 299 }; 300 301 ir::FilterExpression::WithComponentDataRange { 302 component: C::component_name().to_owned(), 303 start, 304 end, 305 } 306 } 307} 308 309impl<C: QueryFilterValue + Component> QueryFilterValue for std::ops::RangeTo<C> { 310 fn filter_expression(&self) -> ir::FilterExpression { 311 use rusqlite::types::ToSqlOutput; 312 313 let end = match C::to_rusqlite(&self.end).unwrap() { 314 ToSqlOutput::Borrowed(v) => v.to_owned().into(), 315 ToSqlOutput::Owned(v) => v, 316 other => unreachable!("{other:?}"), 317 }; 318 319 ir::FilterExpression::WithComponentDataRange { 320 component: C::component_name().to_owned(), 321 start: rusqlite::types::Value::Null, 322 end, 323 } 324 } 325} 326 327impl<C: QueryFilterValue + Component> QueryFilterValue for std::ops::RangeFrom<C> { 328 fn filter_expression(&self) -> ir::FilterExpression { 329 use rusqlite::types::ToSqlOutput; 330 331 let start = match C::to_rusqlite(&self.start).unwrap() { 332 ToSqlOutput::Borrowed(v) => v.to_owned().into(), 333 ToSqlOutput::Owned(v) => v, 334 other => unreachable!("{other:?}"), 335 }; 336 337 ir::FilterExpression::WithComponentDataRange { 338 component: C::component_name().to_owned(), 339 start, 340 end: rusqlite::types::Value::Null, 341 } 342 } 343} 344 345mod tuples { 346 use super::*; 347 348 macro_rules! query_data_impl { 349 ( $($ts:ident)* ) => { 350 impl<$($ts,)+> QueryData for ($($ts,)+) 351 where 352 $($ts: QueryData,)+ 353 { 354 type Output<'a> = ($($ts::Output<'a>,)+); 355 356 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> { 357 Some(($($ts::from_entity(e)?,)+)) 358 } 359 360 361 fn filter_expression() -> ir::FilterExpression{ 362 ir::FilterExpression::and([ 363 $(<$ts as QueryData>::filter_expression()),+ 364 ]) 365 } 366 } 367 } 368 } 369 370 macro_rules! filter_value_impl { 371 ( $($ts:ident)* ) => { 372 373 impl<$($ts,)+> QueryFilterValue for ($($ts,)+) 374 where 375 $($ts: QueryFilterValue,)+ 376 { 377 378 fn filter_expression(&self) -> ir::FilterExpression{ 379 #[allow(non_snake_case)] 380 let ($($ts,)+) = self; 381 ir::FilterExpression::and([ 382 $($ts.filter_expression(),)+ 383 ]) 384 } 385 } 386 } 387 } 388 389 macro_rules! impl_query_filter { 390 ( $($ts:ident)* ) => { 391 impl<$($ts,)+> QueryFilter for ($($ts,)+) 392 where 393 $($ts: QueryFilter,)+ 394 { 395 396 #[allow(non_snake_case)] 397 fn filter_expression() -> ir::FilterExpression{ 398 ir::FilterExpression::and([ 399 $(<$ts as QueryFilter>::filter_expression()),+ 400 ]) 401 } 402 } 403 404 impl<$($ts,)+> QueryFilter for Or<($($ts,)+)> 405 where 406 $($ts: QueryFilter,)+ 407 { 408 409 #[allow(non_snake_case)] 410 fn filter_expression() -> ir::FilterExpression{ 411 ir::FilterExpression::or([ 412 $(<$ts as QueryFilter>::filter_expression()),+ 413 ]) 414 } 415 } 416 417 impl<$($ts,)+> QueryFilter for With<($($ts,)+)> 418 where 419 $($ts: Component,)+ 420 { 421 422 fn filter_expression() -> ir::FilterExpression{ 423 ir::FilterExpression::and([ 424 $(ir::FilterExpression::with_component($ts::component_name()),)+ 425 ]) 426 } 427 } 428 429 impl<$($ts,)+> QueryFilter for Without<($($ts,)+)> 430 where 431 $($ts: Component,)+ 432 { 433 434 fn filter_expression() -> ir::FilterExpression{ 435 ir::FilterExpression::and([ 436 $(ir::FilterExpression::without_component($ts::component_name()),)+ 437 ]) 438 } 439 } 440 }; 441 } 442 443 crate::tuple_macros::for_each_tuple!(query_data_impl); 444 crate::tuple_macros::for_each_tuple!(filter_value_impl); 445 crate::tuple_macros::for_each_tuple!(impl_query_filter); 446} 447 448#[cfg(test)] 449mod tests { 450 use serde::{Deserialize, Serialize}; 451 452 use super::*; 453 use crate as ecsdb; 454 455 #[derive(Debug, Serialize, Deserialize, Component)] 456 struct A; 457 458 #[derive(Debug, Serialize, Deserialize, Component)] 459 struct B; 460 461 #[test] 462 #[allow(unused)] 463 fn system_fns() { 464 fn sys_a(query: Query<A>) {} 465 fn sys_b(query: Query<(A, Without<B>)>) {} 466 fn sys_c(query: Query<Or<(A, B)>>) {} 467 } 468}