Skip to main content

moirai/query/
spec.rs

1//! Structural query selection and filter authoring.
2//!
3//! Build a [`QuerySpec`] with required components, tag markers, exclusions, change-detection
4//! filters, and optional caller-ordered exact entity ids. Resolution happens against one world schema.
5
6use alloc::vec::Vec;
7use core::any::TypeId;
8
9use crate::component::ComponentId;
10use crate::query::ExactIdPolicy;
11use crate::EntityId;
12
13/// Immutable description of which entities a query should visit and how to filter them.
14#[derive(Clone, Debug, Default)]
15pub struct QuerySpec {
16    pub(crate) required: Vec<TypeId>,
17    pub(crate) required_ids: Vec<ComponentId>,
18    pub(crate) without: Vec<TypeId>,
19    pub(crate) without_ids: Vec<ComponentId>,
20    pub(crate) with_tags: Vec<TypeId>,
21    pub(crate) with_tag_ids: Vec<ComponentId>,
22    pub(crate) without_tags: Vec<TypeId>,
23    pub(crate) without_tag_ids: Vec<ComponentId>,
24    pub(crate) added: Vec<TypeId>,
25    pub(crate) added_ids: Vec<ComponentId>,
26    pub(crate) changed: Vec<TypeId>,
27    pub(crate) changed_ids: Vec<ComponentId>,
28    pub(crate) exact_ids: Option<Vec<EntityId>>,
29    pub(crate) exact_id_policy: Option<ExactIdPolicy>,
30}
31
32impl QuerySpec {
33    /// Empty spec that matches entities with no additional structural constraints.
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    /// Require a registered data component type.
39    pub fn with<T: 'static>(mut self) -> Self {
40        self.required.push(TypeId::of::<T>());
41        self
42    }
43
44    /// Require a registered data component by [`ComponentId`].
45    pub fn with_id(mut self, id: ComponentId) -> Self {
46        self.required_ids.push(id);
47        self
48    }
49
50    /// Exclude entities that have the component type.
51    pub fn without<T: 'static>(mut self) -> Self {
52        self.without.push(TypeId::of::<T>());
53        self
54    }
55
56    /// Exclude entities that have the component id.
57    pub fn without_id(mut self, id: ComponentId) -> Self {
58        self.without_ids.push(id);
59        self
60    }
61
62    /// Require a zero-sized tag marker type.
63    pub fn with_tag<T: 'static>(mut self) -> Self {
64        self.with_tags.push(TypeId::of::<T>());
65        self
66    }
67
68    /// Require a registered tag marker by [`ComponentId`].
69    pub fn with_tag_id(mut self, id: ComponentId) -> Self {
70        self.with_tag_ids.push(id);
71        self
72    }
73
74    /// Exclude entities that carry the tag marker type.
75    pub fn without_tag<T: 'static>(mut self) -> Self {
76        self.without_tags.push(TypeId::of::<T>());
77        self
78    }
79
80    /// Exclude entities that carry the tag marker id.
81    pub fn without_tag_id(mut self, id: ComponentId) -> Self {
82        self.without_tag_ids.push(id);
83        self
84    }
85
86    /// Require the component to be added inside the active change window.
87    pub fn added<T: 'static>(mut self) -> Self {
88        self.added.push(TypeId::of::<T>());
89        self
90    }
91
92    /// Require the component id to be added inside the active change window.
93    pub fn added_id(mut self, id: ComponentId) -> Self {
94        self.added_ids.push(id);
95        self
96    }
97
98    /// Require the component to change inside the active change window.
99    pub fn changed<T: 'static>(mut self) -> Self {
100        self.changed.push(TypeId::of::<T>());
101        self
102    }
103
104    /// Require the component id to change inside the active change window.
105    pub fn changed_id(mut self, id: ComponentId) -> Self {
106        self.changed_ids.push(id);
107        self
108    }
109
110    /// Visit only the listed entity ids in caller order.
111    pub fn exact_ids(mut self, ids: Vec<EntityId>, policy: ExactIdPolicy) -> Self {
112        self.exact_ids = Some(ids);
113        self.exact_id_policy = Some(policy);
114        self
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use crate::component::ComponentOptions;
122    use crate::world::WorldBuilder;
123
124    #[derive(Clone, Copy)]
125    struct Position;
126
127    #[derive(Clone, Copy)]
128    struct Player;
129
130    #[test]
131    fn dynamic_id_builders_populate_each_selector_group() {
132        let mut builder = WorldBuilder::new();
133        let position = builder
134            .register_component::<Position>(ComponentOptions::sparse())
135            .expect("position");
136        let player = builder
137            .register_component::<Player>(ComponentOptions::tag())
138            .expect("player");
139
140        let spec = QuerySpec::new()
141            .with_id(position.clone())
142            .without_id(position.clone())
143            .with_tag_id(player.clone())
144            .without_tag_id(player)
145            .added_id(position.clone())
146            .changed_id(position);
147
148        assert_eq!(spec.required_ids.len(), 1);
149        assert_eq!(spec.without_ids.len(), 1);
150        assert_eq!(spec.with_tag_ids.len(), 1);
151        assert_eq!(spec.without_tag_ids.len(), 1);
152        assert_eq!(spec.added_ids.len(), 1);
153        assert_eq!(spec.changed_ids.len(), 1);
154    }
155}