khora_data/ecs/
query_plan.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Defines the `QueryPlan`, which pre-calculates the most efficient way to execute an ECS query.
16
17use crate::ecs::SemanticDomain;
18use std::{any::TypeId, collections::HashSet};
19
20/// Defines whether a query can be executed "natively" or requires a transversal join.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum QueryMode {
23    /// Optimal path: all requested components are in the same domain.
24    /// Iteration happens linearly over matching `ComponentPage`s.
25    Native,
26    /// Join path: requested components span multiple domains.
27    /// Iteration is "driven" by one domain and peers are looked up via bitsets.
28    Transversal,
29}
30
31/// A pre-calculated execution strategy for a specific set of component types.
32///
33/// This plan is cached in the `World` to avoid re-analyzing query signatures
34/// on every frame.
35#[derive(Debug, Clone)]
36pub struct QueryPlan {
37    /// The execution mode (Native or Transversal).
38    pub mode: QueryMode,
39    /// If Transversal, this is the domain that drives the iteration.
40    pub driver_domain: Option<SemanticDomain>,
41    /// If Transversal, these are the peer domains that must be joined.
42    pub peer_domains: HashSet<SemanticDomain>,
43    /// The signature used to find matching pages (driver domain components).
44    pub driver_signature: Vec<TypeId>,
45}
46
47impl QueryPlan {
48    /// Creates a new `QueryPlan` for the given component types.
49    pub fn new(
50        is_transversal: bool,
51        driver_domain: Option<SemanticDomain>,
52        peer_domains: HashSet<SemanticDomain>,
53        driver_signature: Vec<TypeId>,
54    ) -> Self {
55        Self {
56            mode: if is_transversal {
57                QueryMode::Transversal
58            } else {
59                QueryMode::Native
60            },
61            driver_domain,
62            peer_domains,
63            driver_signature,
64        }
65    }
66}