Skip to main content

gloss_hecs/
query_one.rs

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