edict/system/func/
view.rs

1use core::{
2    any::{type_name, TypeId},
3    marker::PhantomData,
4    ptr::NonNull,
5};
6
7use crate::{
8    archetype::Archetype,
9    component::ComponentInfo,
10    query::SendQuery,
11    system::ActionBufferQueue,
12    view::{acquire, release, RuntimeBorrowState, StaticallyBorrowed, View, ViewCell, ViewValue},
13    world::World,
14    Access,
15};
16
17use super::{FnArg, FnArgState};
18
19/// Query suitable for [`View`] args for function-systems.
20pub trait QueryArg: SendQuery {
21    /// Creates new query state.
22    fn new() -> Self;
23
24    /// Hook called before function-system runs to update the state.
25    #[inline(always)]
26    fn before(&mut self, world: &World) {
27        let _ = world;
28    }
29
30    /// Hook called after function-system runs to update the state.
31    #[inline(always)]
32    fn after(&mut self, world: &World) {
33        let _ = world;
34    }
35}
36
37/// State type used by corresponding [`View`].
38#[derive(Default)]
39pub struct ViewState<Q, F, B> {
40    query: Q,
41    filter: F,
42    marker: PhantomData<B>,
43}
44
45impl<'a, Q, F> FnArg for ViewValue<'a, Q, F, RuntimeBorrowState>
46where
47    Q: QueryArg,
48    F: QueryArg,
49{
50    type State = ViewState<Q, F, RuntimeBorrowState>;
51}
52
53unsafe impl<Q, F> FnArgState for ViewState<Q, F, RuntimeBorrowState>
54where
55    Q: QueryArg,
56    F: QueryArg,
57{
58    type Arg<'a> = ViewValue<'a, Q, F, RuntimeBorrowState>;
59
60    #[inline(always)]
61    fn new() -> Self {
62        ViewState {
63            query: Q::new(),
64            filter: F::new(),
65            marker: PhantomData,
66        }
67    }
68
69    #[inline(always)]
70    fn is_local(&self) -> bool {
71        false
72    }
73
74    #[inline(always)]
75    fn world_access(&self) -> Option<Access> {
76        Some(Access::Read)
77    }
78
79    #[inline(always)]
80    fn visit_archetype(&self, archetype: &Archetype) -> bool {
81        self.query.visit_archetype(archetype) && self.filter.visit_archetype(archetype)
82    }
83
84    #[inline(always)]
85    fn borrows_components_at_runtime(&self) -> bool {
86        true
87    }
88
89    #[inline(always)]
90    fn component_access(&self, comp: &ComponentInfo) -> Option<Access> {
91        let q = self
92            .query
93            .component_access(comp)
94            .unwrap_or(Some(Access::Write));
95        let f = self
96            .filter
97            .component_access(comp)
98            .unwrap_or(Some(Access::Write));
99
100        match (q, f) {
101            (None, one) | (one, None) => one,
102            (Some(Access::Read), Some(Access::Read)) => Some(Access::Read),
103            _ => {
104                // This view uses runtime borrow, so conflict can be resolved at runtime.
105                Some(Access::Write)
106            }
107        }
108    }
109
110    #[inline(always)]
111    fn resource_type_access(&self, _ty: TypeId) -> Option<Access> {
112        None
113    }
114
115    #[inline(always)]
116    unsafe fn get_unchecked<'a>(
117        &'a mut self,
118        world: NonNull<World>,
119        _queue: &mut dyn ActionBufferQueue,
120    ) -> ViewCell<'a, Q, F> {
121        // Safety: Declares read access.
122        let world = unsafe { world.as_ref() };
123        self.query.before(world);
124        self.filter.before(world);
125        ViewValue::new_cell(world, self.query, self.filter)
126    }
127
128    #[inline(always)]
129    unsafe fn flush_unchecked(
130        &mut self,
131        world: NonNull<World>,
132        _queue: &mut dyn ActionBufferQueue,
133    ) {
134        // Safety: Declares read access.
135        let world = unsafe { world.as_ref() };
136        self.query.after(world);
137        self.filter.after(world);
138    }
139}
140
141impl<'a, Q, F> FnArg for ViewValue<'a, Q, F, StaticallyBorrowed>
142where
143    Q: QueryArg,
144    F: QueryArg,
145{
146    type State = ViewState<Q, F, StaticallyBorrowed>;
147}
148
149unsafe impl<Q, F> FnArgState for ViewState<Q, F, StaticallyBorrowed>
150where
151    Q: QueryArg,
152    F: QueryArg,
153{
154    type Arg<'a> = ViewValue<'a, Q, F, StaticallyBorrowed>;
155
156    #[inline(always)]
157    fn new() -> Self {
158        ViewState {
159            query: Q::new(),
160            filter: F::new(),
161            marker: PhantomData,
162        }
163    }
164
165    #[inline(always)]
166    fn is_local(&self) -> bool {
167        false
168    }
169
170    #[inline(always)]
171    fn world_access(&self) -> Option<Access> {
172        Some(Access::Read)
173    }
174
175    #[inline(always)]
176    fn visit_archetype(&self, archetype: &Archetype) -> bool {
177        self.query.visit_archetype(archetype) && self.filter.visit_archetype(archetype)
178    }
179
180    #[inline(always)]
181    fn borrows_components_at_runtime(&self) -> bool {
182        false
183    }
184
185    #[inline(always)]
186    fn component_access(&self, comp: &ComponentInfo) -> Option<Access> {
187        let Ok(q) = self.query.component_access(comp) else {
188            panic!("Mutable alias in query of `{}`", type_name::<Self>());
189        };
190        let Ok(f) = self.filter.component_access(comp) else {
191            panic!("Mutable alias in filter of `{}`", type_name::<Self>());
192        };
193
194        match (q, f) {
195            (None, one) | (one, None) => one,
196            (Some(Access::Read), Some(Access::Read)) => Some(Access::Read),
197            _ => {
198                panic!(
199                    "Conflicting query and filter in `{}`.
200                        A component is aliased mutably.",
201                    core::any::type_name::<Self>()
202                );
203            }
204        }
205    }
206
207    #[inline(always)]
208    fn resource_type_access(&self, _ty: TypeId) -> Option<Access> {
209        None
210    }
211
212    #[inline(always)]
213    unsafe fn get_unchecked<'a>(
214        &'a mut self,
215        world: NonNull<World>,
216        _queue: &mut dyn ActionBufferQueue,
217    ) -> View<'a, Q, F> {
218        // Safety: Declares read access.
219        let world = unsafe { world.as_ref() };
220
221        #[cfg(debug_assertions)]
222        acquire(self.query, self.filter, world.archetypes());
223
224        self.query.before(world);
225        self.filter.before(world);
226
227        // Safety: Declares access for these queries.
228        unsafe { ViewValue::new_static(world, self.query, self.filter) }
229    }
230
231    #[inline(always)]
232    unsafe fn flush_unchecked(
233        &mut self,
234        world: NonNull<World>,
235        _queue: &mut dyn ActionBufferQueue,
236    ) {
237        // Safety: Declares read access.
238        let world = unsafe { world.as_ref() };
239
240        #[cfg(debug_assertions)]
241        release(self.query, self.filter, world.archetypes());
242
243        self.query.after(world);
244        self.filter.after(world);
245    }
246}
247
248#[test]
249fn test_system() {
250    fn foo(_: ViewCell<&u32>) {}
251    fn is_system<M, T: super::IntoSystem<M>>(_: T) {}
252    is_system(foo);
253}