Experiments in applying Entity-Component-System patterns to durable data storage APIs.
1pub mod component;
2
3pub use component::Bundle;
4pub use component::{Component, ComponentRead, ComponentWrite};
5
6pub mod dyn_component;
7pub use dyn_component::DynComponent;
8
9pub mod entity;
10pub use entity::{Entity, NewEntity};
11
12pub mod extension;
13pub use extension::Extension;
14
15pub mod hierarchy;
16
17pub mod query;
18
19pub mod resource;
20pub use resource::*;
21
22pub mod schedule;
23pub use schedule::Schedule;
24
25pub mod sqlite_ext;
26
27pub mod system;
28
29pub mod rusqlite {
30 pub use rusqlite::*;
31}
32
33use self_cell::MutBorrow;
34use serde::{Deserialize, Serialize};
35pub use system::*;
36
37mod tuple_macros;
38
39use std::borrow::Cow;
40use std::path::Path;
41
42use tracing::{debug, instrument};
43
44use crate::query::QueryFilterValue;
45
46pub type EntityId = i64;
47
48#[derive(Debug, thiserror::Error)]
49pub enum Error {
50 #[error("Database Error: {0}")]
51 Database(#[from] rusqlite::Error),
52 #[error(transparent)]
53 ComponentStorage(#[from] component::StorageError),
54}
55
56pub struct Ecs {
57 conn: rusqlite::Connection,
58 extensions: anymap::Map<dyn anymap::any::Any + Send>,
59}
60
61impl Ecs {
62 pub fn open_in_memory() -> Result<Self, Error> {
63 Self::from_rusqlite(rusqlite::Connection::open_in_memory()?)
64 }
65
66 pub fn open(path: impl AsRef<Path>) -> Result<Self, Error> {
67 Self::from_rusqlite(rusqlite::Connection::open(path)?)
68 }
69
70 pub fn from_rusqlite(mut conn: rusqlite::Connection) -> Result<Self, Error> {
71 conn.pragma_update(None, "journal_mode", "wal")?;
72 conn.execute_batch(include_str!("schema.sql"))?;
73 conn.set_transaction_behavior(::rusqlite::TransactionBehavior::Immediate);
74
75 sqlite_ext::add_regexp_function(&conn)?;
76
77 Ok(Self {
78 conn,
79 extensions: anymap::Map::new(),
80 })
81 }
82}
83
84impl Ecs {
85 pub fn close(self) -> Result<(), Error> {
86 self.conn.close().map_err(|(_conn, e)| Error::Database(e))
87 }
88
89 pub fn data_version(&self) -> Result<i64, Error> {
90 Ok(self
91 .conn
92 .query_row("select data_version from pragma_data_version", [], |x| {
93 x.get("data_version")
94 })?)
95 }
96}
97
98impl Ecs {
99 pub fn new_entity<'a>(&'a self) -> NewEntity<'a> {
100 Entity::without_id(self)
101 }
102
103 pub fn entity<'a>(&'a self, eid: EntityId) -> Entity<'a> {
104 Entity::with_id(self, eid)
105 }
106}
107
108impl Ecs {
109 pub fn entity_with<'a, B: Bundle>(&'a self, eid: EntityId) -> Option<Entity<'a>> {
110 let e = Entity::with_id(self, eid);
111 e.has::<B>().then_some(e)
112 }
113}
114
115impl Ecs {
116 pub fn query<'a, D, F>(&'a self) -> impl Iterator<Item = D::Output<'a>> + 'a
117 where
118 D: query::QueryData + 'a,
119 F: query::QueryFilter + 'a,
120 {
121 self.try_query::<D, F>().unwrap()
122 }
123
124 #[instrument(name = "query", level = "debug", skip_all)]
125 pub fn try_query<'a, D, F>(&'a self) -> Result<impl Iterator<Item = D::Output<'a>> + 'a, Error>
126 where
127 D: query::QueryData + 'a,
128 F: query::QueryFilter + 'a,
129 {
130 debug!(
131 data = std::any::type_name::<D>(),
132 filter = std::any::type_name::<F>()
133 );
134 let query = query::Query::<D, F>::new(self);
135 query.try_iter()
136 }
137
138 pub fn query_filtered<'a, D, F>(
139 &'a self,
140 filter: impl QueryFilterValue + 'a,
141 ) -> impl Iterator<Item = D::Output<'a>> + 'a
142 where
143 D: query::QueryData + 'a,
144 F: query::QueryFilter + 'a,
145 {
146 self.try_query_fitered::<D, F>(filter).unwrap()
147 }
148
149 #[instrument(name = "find", level = "debug", skip_all)]
150 pub fn try_query_fitered<'a, D, F>(
151 &'a self,
152 filter_value: impl query::QueryFilterValue + 'a,
153 ) -> Result<impl Iterator<Item = D::Output<'a>> + 'a, Error>
154 where
155 D: query::QueryData + 'a,
156 F: query::QueryFilter + 'a,
157 {
158 let query = query::Query::<D, F, _>::with_filter(self, filter_value);
159 query.try_iter()
160 }
161}
162
163impl Ecs {
164 pub fn find<'a>(
165 &'a self,
166 filter_value: impl query::QueryFilterValue + 'a,
167 ) -> impl Iterator<Item = Entity<'a>> + 'a {
168 self.try_find(filter_value).unwrap()
169 }
170
171 pub fn try_find<'a>(
172 &'a self,
173 filter_value: impl query::QueryFilterValue + 'a,
174 ) -> Result<impl Iterator<Item = Entity<'a>> + 'a, Error> {
175 self.try_query_fitered::<Entity<'a>, ()>(filter_value)
176 }
177}
178
179#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
180pub struct CreatedAt(pub chrono::DateTime<chrono::Utc>);
181
182#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
183pub struct LastUpdated(pub chrono::DateTime<chrono::Utc>);
184
185impl Ecs {
186 #[instrument(level = "debug", skip_all)]
187 fn fetch<'a, Q: query::QueryData + 'a>(
188 &'a self,
189 sql_query: query::ir::Query,
190 ) -> Result<impl Iterator<Item = Q::Output<'a>> + 'a, Error> {
191 let entity_ids = self.fetch_entity_ids(sql_query)?;
192
193 let rows = entity_ids
194 .into_iter()
195 .scan(self, |ecs, eid| Some(Entity::with_id(ecs, eid)))
196 .map(|e| {
197 debug!(
198 data = std::any::type_name::<Q>(),
199 entity = ?e,
200 "Fetching QueryData"
201 );
202 Q::from_entity(e).unwrap()
203 });
204
205 Ok(rows)
206 }
207
208 fn fetch_entity_ids<'a>(&'a self, sql_query: query::ir::Query) -> Result<Vec<EntityId>, Error> {
209 let (sql, placeholders) = sql_query.into_sql();
210 debug!(sql);
211
212 let mut stmt = self.conn.prepare(&sql)?;
213 let params: Box<[(&str, &dyn rusqlite::ToSql)]> = placeholders
214 .iter()
215 .map(|(p, v)| (p.as_str(), v.as_ref()))
216 .collect();
217
218 let rows = stmt
219 .query_map(¶ms[..], |row| row.get("entity"))?
220 .map(|r| r.expect("EntityId from Query"))
221 .collect();
222
223 Ok(rows)
224 }
225}
226
227#[allow(unused)]
228impl Ecs {
229 #[instrument(level = "debug", skip_all)]
230 fn fetch_lazy<'a, Q: query::QueryData + 'a>(
231 &'a self,
232 sql_query: query::ir::Query,
233 ) -> Result<impl Iterator<Item = Q::Output<'a>> + 'a, Error> {
234 let rows = self
235 .fetch_entity_ids_lazy(sql_query)?
236 .scan(self, |ecs, eid| Some(Entity::with_id(ecs, eid)))
237 .map(|e| {
238 debug!(
239 data = std::any::type_name::<Q>(),
240 entity = ?e,
241 "Fetching QueryData"
242 );
243 Q::from_entity(e).unwrap()
244 });
245
246 Ok(rows)
247 }
248
249 fn fetch_entity_ids_lazy<'a>(
250 &'a self,
251 sql_query: query::ir::Query,
252 ) -> Result<impl Iterator<Item = EntityId> + 'a, Error> {
253 let (sql, placeholders) = sql_query.into_sql();
254 debug!(sql);
255
256 type RowsRef<'a> = ::rusqlite::Rows<'a>;
257
258 self_cell::self_cell!(
259 struct OwningRows<'conn> {
260 owner: self_cell::MutBorrow<::rusqlite::Statement<'conn>>,
261 #[covariant]
262 dependent: RowsRef,
263 }
264 );
265
266 impl<'conn> Iterator for OwningRows<'conn> {
267 type Item = Result<EntityId, rusqlite::Error>;
268 fn next(&mut self) -> Option<Self::Item> {
269 match self.with_dependent_mut(|_stmt, rows| rows.next()) {
270 Ok(Some(row)) => Some(row.get("entity")),
271 Ok(None) => None,
272 Err(e) => Some(Err(e)),
273 }
274 }
275 }
276
277 let stmt = self.conn.prepare(&sql)?;
278 let params: Box<[(&str, &dyn rusqlite::ToSql)]> = placeholders
279 .iter()
280 .map(|(p, v)| (p.as_str(), v.as_ref()))
281 .collect();
282
283 let owning_rows =
284 OwningRows::try_new(MutBorrow::new(stmt), |s| s.borrow_mut().query(¶ms[..]))
285 .unwrap();
286
287 Ok(owning_rows.map(|result| result.expect("EntityId from Row")))
288 }
289}
290
291impl Ecs {
292 pub fn raw_sql<'a>(&'a self) -> &'a rusqlite::Connection {
293 &self.conn
294 }
295}
296
297impl AsRef<chrono::DateTime<chrono::Utc>> for CreatedAt {
298 fn as_ref(&self) -> &chrono::DateTime<chrono::Utc> {
299 &self.0
300 }
301}
302
303impl Component for CreatedAt {
304 type Storage = component::JsonStorage;
305 const NAME: &'static str = "ecsdb::CreatedAt";
306}
307
308impl Default for CreatedAt {
309 fn default() -> Self {
310 Self(chrono::DateTime::<chrono::Utc>::MIN_UTC)
311 }
312}
313
314impl AsRef<chrono::DateTime<chrono::Utc>> for LastUpdated {
315 fn as_ref(&self) -> &chrono::DateTime<chrono::Utc> {
316 &self.0
317 }
318}
319
320impl Component for LastUpdated {
321 type Storage = component::JsonStorage;
322 const NAME: &'static str = "ecsdb::LastUpdated";
323}
324
325impl Default for LastUpdated {
326 fn default() -> Self {
327 Self(chrono::DateTime::<chrono::Utc>::MIN_UTC)
328 }
329}
330
331pub fn system_name<Marker, S: IntoSystem<Marker>>(system: S) -> Cow<'static, str> {
332 system.into_system().name()
333}
334
335#[doc(hidden)]
336pub mod doctests {
337 use crate as ecsdb; // Fixes `#[derive(Component)]`
338
339 pub use crate::{Component, Ecs, Entity, EntityId};
340 pub use serde::{Deserialize, Serialize};
341
342 #[derive(Serialize, Deserialize, Component)]
343 pub struct Marker;
344
345 #[derive(Serialize, Deserialize, Component)]
346 pub struct Date(pub chrono::DateTime<chrono::Utc>);
347
348 #[derive(Serialize, Deserialize, Component)]
349 pub enum State {
350 New,
351 Processing,
352 Finished,
353 }
354
355 #[cfg(doctest)]
356 #[doc = include_str!("../README.md")]
357 pub struct ReadmeDoctests;
358}
359
360#[cfg(test)]
361mod tests {
362 // #[derive(Component)] derives `impl ecsdb::Component for ...`
363 use crate::{self as ecsdb, CreatedAt, Ecs, Entity, EntityId};
364 use crate::{Bundle, Component};
365
366 use anyhow::anyhow;
367 use serde::{Deserialize, Serialize};
368
369 #[derive(Debug, Serialize, Deserialize, Component)]
370 struct MarkerComponent;
371
372 #[derive(Debug, Serialize, Deserialize, PartialEq, Component)]
373 struct ComponentWithData(u64);
374
375 #[derive(Debug, Serialize, Deserialize, Component)]
376 struct A;
377
378 #[derive(Debug, Serialize, Deserialize, PartialEq, Component)]
379 struct B;
380
381 #[derive(Debug, Serialize, Deserialize, PartialEq, Component)]
382 struct C;
383
384 #[test]
385 fn derive_valid_component_name() {
386 assert_eq!(
387 MarkerComponent::component_name(),
388 "ecsdb::tests::MarkerComponent"
389 );
390 assert_eq!(
391 ComponentWithData::component_name(),
392 "ecsdb::tests::ComponentWithData"
393 );
394 }
395
396 #[test]
397 fn entity_attach_detach() {
398 let db = super::Ecs::open_in_memory().unwrap();
399 let entity = db
400 .new_entity()
401 .attach(ComponentWithData(1234))
402 .attach(MarkerComponent);
403
404 assert!(entity.component::<MarkerComponent>().is_some());
405 entity.detach::<MarkerComponent>();
406 assert!(entity.component::<MarkerComponent>().is_none());
407
408 assert_eq!(
409 entity.component::<ComponentWithData>(),
410 Some(ComponentWithData(1234))
411 );
412 }
413
414 #[test]
415 fn component_overwrites() {
416 let db = super::Ecs::open_in_memory().unwrap();
417
418 let entity = db
419 .new_entity()
420 .attach(ComponentWithData(42))
421 .attach(ComponentWithData(23));
422 assert_eq!(entity.component::<ComponentWithData>().unwrap().0, 23);
423 }
424
425 #[test]
426 fn attaching_same_component_in_bundle_overwrites() {
427 let db = super::Ecs::open_in_memory().unwrap();
428
429 let entity = db
430 .new_entity()
431 .attach((ComponentWithData(42), ComponentWithData(23)));
432 assert_eq!(entity.component::<ComponentWithData>().unwrap().0, 23);
433 }
434
435 #[test]
436 fn bundle_struct() {
437 let db = super::Ecs::open_in_memory().unwrap();
438
439 #[derive(Debug, Bundle)]
440 struct Bundle {
441 a: A,
442 b: B,
443 data: ComponentWithData,
444 }
445
446 let bundle = Bundle {
447 a: A,
448 b: B,
449 data: ComponentWithData(1234),
450 };
451
452 let entity = db.new_entity().attach(bundle);
453
454 assert!(entity.has::<(A, B, ComponentWithData)>());
455 }
456
457 #[test]
458 fn bundle_tuplestruct() {
459 let db = super::Ecs::open_in_memory().unwrap();
460
461 #[derive(Debug, Bundle)]
462 struct Bundle(A, B, ComponentWithData);
463
464 let bundle = Bundle(A, B, ComponentWithData(1234));
465 let entity = db.new_entity().attach(bundle);
466
467 assert!(entity.has::<(A, B, ComponentWithData)>());
468 }
469
470 #[test]
471 fn bundle_optionals() {
472 let db = super::Ecs::open_in_memory().unwrap();
473
474 #[derive(Debug, Bundle)]
475 struct Bundle(A, Option<B>);
476
477 let entity = db.new_entity().attach(Bundle(A, None));
478 assert!(!entity.has::<B>());
479
480 entity.attach(Bundle(A, Some(B)));
481 assert!(entity.has::<B>());
482
483 entity.attach(Bundle(A, None));
484 assert!(entity.has::<B>());
485 }
486
487 use super::query::*;
488
489 #[test]
490 fn query_tuples() {
491 let db = super::Ecs::open_in_memory().unwrap();
492 let _: Vec<MarkerComponent> = db.query::<MarkerComponent, ()>().collect();
493
494 let _: Vec<Entity> = db.query::<Entity, ()>().collect();
495 let _: Vec<(Entity, MarkerComponent)> =
496 db.query::<(Entity, MarkerComponent), ()>().collect();
497
498 let _: Vec<Entity> = db.query::<Entity, With<MarkerComponent>>().collect();
499 let _: Vec<Entity> = db.query::<Entity, Without<MarkerComponent>>().collect();
500 let _: Vec<MarkerComponent> = db
501 .query::<MarkerComponent, Or<(
502 Without<(MarkerComponent, MarkerComponent)>,
503 With<(MarkerComponent, MarkerComponent)>,
504 Or<(With<MarkerComponent>, Without<MarkerComponent>)>,
505 )>>()
506 .collect();
507
508 let _: Vec<MarkerComponent> = db
509 .query::<MarkerComponent, (
510 With<(MarkerComponent, ComponentWithData)>,
511 Without<(MarkerComponent, MarkerComponent)>,
512 )>()
513 .collect();
514
515 let _: Vec<MarkerComponent> = db
516 .query::<MarkerComponent, Without<ComponentWithData>>()
517 .collect();
518
519 let _: Vec<()> = db
520 .query::<(), (
521 With<MarkerComponent>,
522 With<MarkerComponent>,
523 With<MarkerComponent>,
524 With<MarkerComponent>,
525 With<MarkerComponent>,
526 With<MarkerComponent>,
527 Without<MarkerComponent>,
528 With<MarkerComponent>,
529 )>()
530 .collect();
531 }
532
533 #[test]
534 fn query() {
535 let db = super::Ecs::open_in_memory().unwrap();
536
537 db.new_entity()
538 .attach(MarkerComponent)
539 .attach(ComponentWithData(1234));
540
541 db.new_entity().attach(ComponentWithData(1234));
542
543 assert_eq!(db.query::<EntityId, ()>().count(), 2);
544 assert_eq!(db.query::<EntityId, MarkerComponent>().count(), 1);
545 assert_eq!(db.query::<EntityId, MarkerComponent>().count(), 1);
546 }
547
548 #[test]
549 fn entity_match_filtered() {
550 let db = super::Ecs::open_in_memory().unwrap();
551
552 db.new_entity()
553 .attach(MarkerComponent)
554 .attach(ComponentWithData(1234));
555
556 db.new_entity().attach(ComponentWithData(1234));
557
558 assert_eq!(db.query::<EntityId, Without<MarkerComponent>>().count(), 1);
559 assert_eq!(
560 db.query::<EntityId, (MarkerComponent, ComponentWithData)>()
561 .count(),
562 1
563 );
564 assert_eq!(
565 db.query::<MarkerComponent, Without<MarkerComponent>>()
566 .count(),
567 0
568 );
569
570 assert_eq!(
571 db.query::<MarkerComponent, (
572 Without<MarkerComponent>,
573 Or<(With<MarkerComponent>, With<ComponentWithData>)>
574 )>()
575 .count(),
576 0
577 );
578 assert_eq!(db.query::<EntityId, ComponentWithData>().count(), 2);
579 }
580
581 #[test]
582 fn or() {
583 let db = Ecs::open_in_memory().unwrap();
584 let a = db.new_entity().attach(A).id();
585 let b = db.new_entity().attach(B).id();
586 let c = db.new_entity().attach(C).id();
587
588 assert_eq!(
589 db.query::<EntityId, Or<(With<A>, With<B>, With<C>)>>()
590 .collect::<Vec<_>>(),
591 vec![a, b, c]
592 );
593 assert_eq!(
594 db.query::<EntityId, Or<(With<A>, With<B>)>>()
595 .collect::<Vec<_>>(),
596 vec![a, b]
597 );
598 assert_eq!(
599 db.query::<EntityId, Or<(With<A>,)>>().collect::<Vec<_>>(),
600 vec![a]
601 );
602 assert_eq!(
603 db.query::<EntityId, Or<(With<B>,)>>().collect::<Vec<_>>(),
604 vec![b]
605 );
606 }
607
608 #[test]
609 fn query_any_of() {
610 let db = Ecs::open_in_memory().unwrap();
611 let a = db.new_entity().attach(A).id();
612 let b = db.new_entity().attach(B).id();
613 let c = db.new_entity().attach(C).id();
614
615 assert_eq!(
616 db.query::<EntityId, AnyOf<(A, B)>>().collect::<Vec<_>>(),
617 vec![a, b]
618 );
619
620 assert_eq!(
621 db.query::<EntityId, AnyOf<(A, C)>>().collect::<Vec<_>>(),
622 vec![a, c]
623 );
624
625 assert_eq!(
626 db.query::<EntityId, AnyOf<(A,)>>().collect::<Vec<_>>(),
627 vec![a]
628 );
629 }
630
631 #[test]
632 fn query_filtered() {
633 let db = Ecs::open_in_memory().unwrap();
634 let eid = db.new_entity().attach(ComponentWithData(123)).id();
635 let _ = db.new_entity().attach(ComponentWithData(123));
636 let _ = db.new_entity().attach(ComponentWithData(255));
637
638 assert_eq!(db.query_filtered::<EntityId, ()>(eid).count(), 1);
639 assert_eq!(
640 db.query_filtered::<EntityId, ()>(eid).collect::<Vec<_>>(),
641 vec![eid]
642 );
643 assert_eq!(
644 db.query_filtered::<EntityId, ()>((eid, MarkerComponent))
645 .count(),
646 0
647 );
648 assert_eq!(
649 db.query_filtered::<EntityId, ()>(MarkerComponent).count(),
650 0
651 );
652 assert_eq!(
653 db.query_filtered::<EntityId, ()>(ComponentWithData(0))
654 .count(),
655 0
656 );
657 assert_eq!(
658 db.query_filtered::<EntityId, ()>(ComponentWithData(123))
659 .count(),
660 2
661 );
662 assert_eq!(
663 db.query_filtered::<EntityId, ()>(ComponentWithData(255))
664 .count(),
665 1
666 );
667
668 let _ = db
669 .new_entity()
670 .attach(MarkerComponent)
671 .attach(ComponentWithData(12345));
672 assert_eq!(
673 db.query_filtered::<EntityId, ()>((MarkerComponent, ComponentWithData(12345)))
674 .count(),
675 1
676 );
677 }
678
679 #[test]
680 fn find_ranges() {
681 let db = Ecs::open_in_memory().unwrap();
682 for n in 0..100 {
683 let _ = db.new_entity().attach(ComponentWithData(n));
684 }
685
686 let mut res = db
687 .query_filtered::<Entity, ()>(ComponentWithData(0)..ComponentWithData(10))
688 .map(|e| e.component::<ComponentWithData>().unwrap());
689 assert_eq!(res.next(), Some(ComponentWithData(0)));
690 assert_eq!(res.last(), Some(ComponentWithData(10)));
691
692 let mut res = db
693 .query_filtered::<Entity, ()>(ComponentWithData(10)..)
694 .map(|e| e.component::<ComponentWithData>().unwrap());
695 assert_eq!(res.next(), Some(ComponentWithData(10)));
696 assert_eq!(res.last(), Some(ComponentWithData(99)));
697
698 let mut res = db
699 .query_filtered::<Entity, ()>(..ComponentWithData(10))
700 .map(|e| e.component::<ComponentWithData>().unwrap());
701 assert_eq!(res.next(), Some(ComponentWithData(0)));
702 assert_eq!(res.last(), Some(ComponentWithData(10)));
703 }
704
705 // #[test]
706 // fn parent() {
707 // let db = Ecs::open_in_memory().unwrap();
708
709 // let parent = db.new_entity().attach(A);
710 // let child1 = db.new_entity().attach(A).attach(BelongsTo(parent.id()));
711 // let child2 = db.new_entity().attach(A).attach(BelongsTo(child1.id()));
712
713 // assert!(parent.parent().is_none());
714 // assert_eq!(child1.parent().map(|e| e.id()), Some(parent.id()));
715 // assert_eq!(child2.parent().map(|e| e.id()), Some(child1.id()));
716
717 // assert_eq!(
718 // child2.parents().map(|e| e.id()).collect::<Vec<_>>(),
719 // vec![child1.id(), parent.id()]
720 // );
721 // }
722
723 #[test]
724 fn enum_component() {
725 #[derive(Serialize, Deserialize, Component, PartialEq, Debug)]
726 enum Foo {
727 A,
728 B,
729 }
730
731 let db = Ecs::open_in_memory().unwrap();
732 let entity = db.new_entity().attach(Foo::A);
733 assert_eq!(entity.component::<Foo>().unwrap(), Foo::A);
734 }
735
736 #[test]
737 fn blob_component() {
738 #[derive(Component, Debug, PartialEq, Clone)]
739 #[component(storage = "blob")]
740 struct X(Vec<u8>);
741
742 impl AsRef<[u8]> for X {
743 fn as_ref(&self) -> &[u8] {
744 self.0.as_slice()
745 }
746 }
747
748 impl From<Vec<u8>> for X {
749 fn from(value: Vec<u8>) -> Self {
750 Self(value)
751 }
752 }
753
754 let x = X(b"asdfasdf".into());
755
756 let db = Ecs::open_in_memory().unwrap();
757 let entity = db.new_entity().attach(x.clone());
758
759 assert_eq!(entity.component::<X>().unwrap(), x.clone());
760 }
761
762 #[test]
763 fn has_many() {
764 let db = Ecs::open_in_memory().unwrap();
765 let a = db.new_entity().attach(A);
766 assert!(a.has::<A>());
767 assert!(!a.has::<B>());
768
769 assert!(a.has::<(A,)>());
770 assert!(!a.has::<(A, B)>());
771 assert!(!a.has::<(A, B, A)>());
772
773 let ab = db.new_entity().attach(A).attach(B);
774 assert!(ab.has::<(A, B)>());
775 assert!(ab.has::<(A, A)>());
776 assert!(ab.has::<(A, B, A)>());
777 }
778
779 #[test]
780 fn from_component_composite() -> Result<(), anyhow::Error> {
781 #[derive(Serialize, Deserialize, Component)]
782 struct A;
783 #[derive(Serialize, Deserialize, Component)]
784 struct B;
785
786 let db = super::Ecs::open_in_memory()?;
787 let _e = db.new_entity().attach((A, B));
788
789 // let ab = e.component::<(A, B)>();
790 // assert!(ab.is_some());
791
792 Ok(())
793 }
794
795 #[test]
796 fn entity_matches() {
797 #[derive(Serialize, Deserialize, Component)]
798 struct A;
799 #[derive(Serialize, Deserialize, Component)]
800 struct B;
801
802 let db = super::Ecs::open_in_memory().unwrap();
803 let e = db.new_entity().attach(A);
804 let e2 = db.new_entity().attach((A, B));
805
806 assert!(e.matches::<With<A>>());
807 assert!(!e.matches::<With<B>>());
808 assert!(!e.matches::<With<(A, B)>>());
809
810 assert!(e2.matches::<With<A>>());
811 assert!(e2.matches::<With<B>>());
812 assert!(e2.matches::<With<(A, B)>>());
813 }
814
815 // #[test]
816 // fn entity_matches_filtered() {
817 // #[derive(Serialize, Deserialize, Component)]
818 // struct A;
819 // #[derive(Serialize, Deserialize, Component)]
820 // struct B;
821
822 // let db = super::Ecs::open_in_memory().unwrap();
823 // let e = db.new_entity().attach(A);
824 // let e2 = db.new_entity().attach((A, B));
825
826 // assert!(e.matches_filtered((With::<A>::default(), e.id())));
827 // assert!(!e.matches::<With<B>>());
828 // assert!(!e.matches::<With<(A, B)>>());
829
830 // assert!(e2.matches::<With<A>>());
831 // assert!(e2.matches::<With<B>>());
832 // assert!(e2.matches::<With<(A, B)>>());
833 // }
834
835 #[test]
836 fn last_modified() {
837 #[derive(Serialize, Deserialize, Component)]
838 struct A;
839 #[derive(Serialize, Deserialize, Component)]
840 struct B;
841
842 let db = super::Ecs::open_in_memory().unwrap();
843 let e = db.new_entity().attach(A);
844
845 assert!(e.last_modified() > chrono::Utc::now() - chrono::Duration::minutes(1));
846
847 let old = e.last_modified();
848
849 std::thread::sleep(std::time::Duration::from_millis(2));
850
851 e.attach(B);
852 assert!(e.last_modified() > old);
853 }
854
855 #[test]
856 fn created() {
857 #[derive(Serialize, Deserialize, Component)]
858 struct A;
859 #[derive(Serialize, Deserialize, Component)]
860 struct B;
861
862 let db = super::Ecs::open_in_memory().unwrap();
863 let e = db.new_entity().attach(A);
864
865 assert!(e.has::<CreatedAt>());
866 assert_ne!(e.created_at(), chrono::DateTime::<chrono::Utc>::MIN_UTC);
867
868 assert!(e.created_at() > chrono::Utc::now() - chrono::Duration::minutes(1),);
869
870 let old = e.created_at();
871
872 std::thread::sleep(std::time::Duration::from_millis(2));
873
874 e.attach(B);
875 assert_eq!(e.created_at(), old);
876 }
877
878 #[test]
879 fn modify_component() -> Result<(), anyhow::Error> {
880 let ecs = super::Ecs::open_in_memory()?;
881
882 #[derive(Component, Debug, Default, Deserialize, Serialize, PartialEq)]
883 struct Foo(Vec<u64>);
884
885 let entity = ecs.new_entity().modify_component(|foo: &mut Foo| {
886 *foo = Foo(vec![1, 2, 3]);
887 });
888 assert_eq!(entity.component(), Some(Foo(vec![1, 2, 3])));
889
890 let entity = ecs
891 .new_entity()
892 .attach(Foo(vec![1, 2, 3]))
893 .modify_component(|foo: &mut Foo| {
894 foo.0.clear();
895 });
896
897 assert_eq!(entity.component(), Some(Foo(vec![])));
898
899 Ok(())
900 }
901
902 #[test]
903 fn try_modify_component() -> Result<(), anyhow::Error> {
904 let ecs = super::Ecs::open_in_memory()?;
905
906 #[derive(Component, Debug, Default, Deserialize, Serialize, PartialEq)]
907 struct Foo(Vec<u64>);
908
909 assert!(
910 ecs.new_entity()
911 .try_modify_component(|_foo: &mut Foo| { Err(anyhow!("error")) })
912 .is_err()
913 );
914
915 assert!(
916 ecs.new_entity()
917 .attach(Foo(vec![1, 2, 3]))
918 .try_modify_component(|_foo: &mut Foo| { Err(anyhow!("error")) })
919 .is_err()
920 );
921
922 Ok(())
923 }
924}