hv_ecs/
query_one.rs

1use core::marker::PhantomData;
2
3use crate::query::{Fetch, With, Without};
4use crate::{Archetype, Component, Query, QueryItem};
5
6/// A borrow of a [`World`](crate::World) sufficient to execute the query `Q` on a single entity
7pub struct QueryOne<'a, Q: Query> {
8    archetype: &'a Archetype,
9    index: u32,
10    borrowed: bool,
11    _marker: PhantomData<Q>,
12}
13
14impl<'a, Q: Query> QueryOne<'a, Q> {
15    /// Construct a query accessing the entity in `archetype` at `index`
16    ///
17    /// # Safety
18    ///
19    /// `index` must be in-bounds for `archetype`
20    pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self {
21        Self {
22            archetype,
23            index,
24            borrowed: false,
25            _marker: PhantomData,
26        }
27    }
28
29    /// Get the query result, or `None` if the entity does not satisfy the query
30    ///
31    /// Must be called at most once.
32    ///
33    /// Panics if called more than once or if it would construct a borrow that clashes with another
34    /// pre-existing borrow.
35    // Note that this uses self's lifetime, not 'a, for soundness.
36    pub fn get(&mut self) -> Option<QueryItem<'_, Q>> {
37        if self.borrowed {
38            panic!("called QueryOnce::get twice; construct a new query instead");
39        }
40        unsafe {
41            let state = Q::Fetch::prepare(self.archetype)?;
42            Q::Fetch::borrow(self.archetype, state);
43            let fetch = Q::Fetch::execute(self.archetype, state);
44            self.borrowed = true;
45            Some(fetch.get(self.index as usize))
46        }
47    }
48
49    /// Transform the query into one that requires a certain component without borrowing it
50    ///
51    /// See `QueryBorrow::with` for details.
52    pub fn with<T: Component>(self) -> QueryOne<'a, With<T, Q>> {
53        self.transform()
54    }
55
56    /// Transform the query into one that skips entities having a certain component
57    ///
58    /// See `QueryBorrow::without` for details.
59    pub fn without<T: Component>(self) -> QueryOne<'a, Without<T, Q>> {
60        self.transform()
61    }
62
63    /// Helper to change the type of the query
64    fn transform<R: Query>(mut self) -> QueryOne<'a, R> {
65        let x = QueryOne {
66            archetype: self.archetype,
67            index: self.index,
68            borrowed: self.borrowed,
69            _marker: PhantomData,
70        };
71        // Ensure `Drop` won't fire redundantly
72        self.borrowed = false;
73        x
74    }
75}
76
77impl<Q: Query> Drop for QueryOne<'_, Q> {
78    fn drop(&mut self) {
79        if self.borrowed {
80            let state = Q::Fetch::prepare(self.archetype).unwrap();
81            Q::Fetch::release(self.archetype, state);
82        }
83    }
84}
85
86unsafe impl<Q: Query> Send for QueryOne<'_, Q> {}
87unsafe impl<Q: Query> Sync for QueryOne<'_, Q> {}