Repository: Ralith/hecs
Pull request #17 (2019-12-21 17:23:43)

Query::without


Patch:
diff --git a/src/query.rs b/src/query.rs
index f2681f7..1a6e367 100644
--- a/src/query.rs
+++ b/src/query.rs
@@ -41,6 +41,11 @@ pub trait Fetch<'a>: Sized {
     fn release(archetype: &Archetype);
 
     /// Access the next item in this archetype without bounds checking
+    ///
+    /// # Safety
+    /// - Must only be called after `borrow`
+    /// - `release` must not be called while `'a` is still live
+    /// - bounds-checking must be performed externally
     unsafe fn next(&mut self) -> Self::Item;
 }
 
@@ -132,28 +137,55 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
     }
 }
 
+pub struct Without<Q, T>(PhantomData<(Q, fn(T))>);
+
+impl<Q: Query, T: Component> Query for Without<Q, T> {
+    type Fetch = FetchWithout<Q::Fetch, T>;
+}
+
+#[doc(hidden)]
+pub struct FetchWithout<F, T>(F, PhantomData<fn(T)>);
+
+impl<'a, F: Fetch<'a>, T: Component> Fetch<'a> for FetchWithout<F, T> {
+    type Item = F::Item;
+    fn wants(archetype: &Archetype) -> bool {
+        !archetype.has::<T>()
+    }
+
+    fn borrow(archetype: &Archetype) {
+        F::borrow(archetype)
+    }
+    fn get(archetype: &'a Archetype) -> Option<Self> {
+        if archetype.has::<T>() {
+            return None;
+        }
+        Some(Self(F::get(archetype)?, PhantomData))
+    }
+    fn release(archetype: &Archetype) {
+        F::release(archetype)
+    }
+
+    unsafe fn next(&mut self) -> F::Item {
+        self.0.next()
+    }
+}
+
 /// A borrow of a `World` sufficient to execute the query `Q`
 ///
 /// Note that borrows are not released until this object is dropped.
 pub struct QueryBorrow<'w, Q: Query> {
     meta: &'w [EntityMeta],
     archetypes: &'w [Archetype],
-    executed: bool,
+    borrowed: bool,
     _marker: PhantomData<Q>,
 }
 
 impl<'w, Q: Query> QueryBorrow<'w, Q> {
     pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype]) -> Self {
-        for x in archetypes {
-            // TODO: Release prior borrows on failure?
-            if Q::Fetch::wants(x) {
-                Q::Fetch::borrow(x);
-            }
-        }
         Self {
             meta,
             archetypes,
-            executed: false,
+            borrowed: false,
             _marker: PhantomData,
         }
     }
@@ -162,18 +194,52 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> {
     ///
     /// Must be called only once per query.
     pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q> {
-        if self.executed {
+        if self.borrowed {
             panic!(
                 "called QueryBorrow::iter twice on the same borrow; construct a new query instead"
             );
         }
-        self.executed = true;
+        for x in self.archetypes {
+            // TODO: Release prior borrows on failure?
+            if Q::Fetch::wants(x) {
+                Q::Fetch::borrow(x);
+            }
+        }
+        self.borrowed = true;
         QueryIter {
             borrow: self,
             archetype_index: 0,
             iter: None,
         }
     }
+
+    /// Transform the query into one that skips entities having a certain component
+    ///
+    /// # Example
+    /// ```
+    /// # use hecs::*;
+    /// let mut world = World::new();
+    /// let a = world.spawn((123, true, "abc"));
+    /// let b = world.spawn((456, false));
+    /// let c = world.spawn((42, "def"));
+    /// let entities = world.query::<&i32>()
+    ///     .without::<bool>()
+    ///     .iter()
+    ///     .map(|(e, &i)| (e, i)) // Copy out of the world
+    ///     .collect::<Vec<_>>();
+    /// assert_eq!(entities, &[(c, 42)]);
+    /// ```
+    pub fn without<T: Component>(mut self) -> QueryBorrow<'w, Without<Q, T>> {
+        let x = QueryBorrow {
+            meta: self.meta,
+            archetypes: self.archetypes,
+            borrowed: self.borrowed,
+            _marker: PhantomData,
+        };
+        // Disarm `Drop`
+        self.archetypes = &[];
+        x
+    }
 }
 
 unsafe impl<'w, Q: Query> Send for QueryBorrow<'w, Q> {}
@@ -181,9 +247,11 @@ unsafe impl<'w, Q: Query> Sync for QueryBorrow<'w, Q> {}
 
 impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> {
     fn drop(&mut self) {
-        for x in self.archetypes {
-            if Q::Fetch::wants(x) {
-                Q::Fetch::release(x);
+        if self.borrowed {
+            for x in self.archetypes {
+                if Q::Fetch::wants(x) {
+                    Q::Fetch::release(x);
+                }
             }
         }
     }
diff --git a/src/world.rs b/src/world.rs
index 830c501..321f1c3 100644
--- a/src/world.rs
+++ b/src/world.rs
@@ -108,18 +108,23 @@ impl World {
 
     /// Efficiently iterate over all entities that have certain components
     ///
-    /// Yields `(Entity, Q)` tuples, where `Q` is some query type. A query type is `&T`, `&mut T`, a
-    /// tuple of query types, or an `Option` wrapping a query type, where `T` is any component
-    /// type. Components queried with `&mut` must only appear once. Entities which do not have a
-    /// component type referenced outside of an `Option` will be skipped.
+    /// Calling `iter` on the returned value yields `(Entity, Q)` tuples, where `Q` is some query
+    /// type. A query type is `&T`, `&mut T`, a tuple of query types, or an `Option` wrapping a
+    /// query type, where `T` is any component type. Components queried with `&mut` must only appear
+    /// once. Entities which do not have a component type referenced outside of an `Option` will be
+    /// skipped.
     ///
     /// Entities are yielded in arbitrary order.
     ///
-    /// Constructing a query will panic if it would violate an existing unique reference or
-    /// construct an invalid unique reference. This occurs when two simultaneously-active queries
-    /// could expose the same entity. Simultaneous queries can access the same component type if and
-    /// only if the world contains no entities that have all components required by both queries,
-    /// assuming no other component borrows are outstanding.
+    /// Iterating a query will panic if it would violate an existing unique reference or construct
+    /// an invalid unique reference. This occurs when two simultaneously-active queries could expose
+    /// the same entity. Simultaneous queries can access the same component type if and only if the
+    /// world contains no entities that have all components required by both queries, assuming no
+    /// other component borrows are outstanding.
+    ///
+    /// Iterating a query yields references with lifetimes bound to the object returned here. To
+    /// ensure those are invalidated, the return value of this method must be dropped for its
+    /// borrows to be released.
     ///
     /// # Example
     /// ```


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 467154e..634a251 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -167,7 +167,7 @@ fn illegal_borrow() {
     world.spawn(("abc", 123));
     world.spawn(("def", 456));
 
-    world.query::<(&mut i32, &i32)>();
+    world.query::<(&mut i32, &i32)>().iter();
 }
 
 #[test]
@@ -177,7 +177,7 @@ fn illegal_borrow_2() {
     world.spawn(("abc", 123));
     world.spawn(("def", 456));
 
-    world.query::<(&mut i32, &mut i32)>();
+    world.query::<(&mut i32, &mut i32)>().iter();
 }
 
 #[test]
@@ -264,6 +264,6 @@ fn alias() {
     world.spawn(("abc", 123));
     world.spawn(("def", 456, true));
     let mut q = world.query::<&mut i32>();
-    let a = q.iter().collect::<Vec<_>>();
-    let b = q.iter().collect::<Vec<_>>();
+    let _a = q.iter().collect::<Vec<_>>();
+    let _b = q.iter().collect::<Vec<_>>();
 }