Skip to main content

moirai/query/
error.rs

1//! Query configuration, ownership, borrow, and cache diagnostics.
2
3use alloc::string::String;
4
5/// Failure while resolving, traversing, caching, or mutating through a query.
6#[non_exhaustive]
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub enum QueryError {
9    /// Query referenced a component type that is not registered in the world schema.
10    UnregisteredComponent { name: String },
11    /// Query traversal expected a different storage kind for the named component.
12    WrongStorageKind { name: String },
13    /// Query spec combined incompatible structural or temporal filters.
14    ConflictingFilters { detail: String },
15    /// Mutable traversal requested the same component type more than once.
16    DuplicateMutableComponent { name: String },
17    /// Query handle or cursor belongs to a different world owner.
18    WrongOwner,
19    /// Membership or result cache handle is stale for its slot and generation.
20    StaleCache,
21    /// Cursor, cache, event, or plan does not match the active query configuration.
22    WrongQuery { detail: String },
23    /// Result-cache policy cannot serve added/changed moving windows.
24    MovingChangeWindow,
25    /// Prepared-query materialization policy is incompatible with the resolved plan.
26    UnsupportedCachePolicy { detail: String },
27    /// Exact-id order conflicts with a result cache that reorders matches.
28    ExactIdOrderConflict,
29    /// Exact-id list contains the same entity more than once.
30    DuplicateExactId { entity: crate::EntityId },
31    /// Exact-id policy requires every listed entity to be available.
32    MissingExactId { entity: crate::EntityId },
33    /// Query traversal cannot borrow world state for the requested operation.
34    BorrowConflict { detail: String },
35    /// Deferred command or bundle write was rejected before enqueue.
36    CommandRejected { detail: String },
37    /// Cache handle owner token does not match the active world.
38    OwnerMismatch,
39    /// Mutable traversal stopped early because a callback returned an error.
40    TraversalAborted {
41        entity: crate::EntityId,
42        detail: String,
43    },
44}
45
46#[cfg(feature = "std")]
47impl core::fmt::Display for QueryError {
48    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
49        match self {
50            Self::UnregisteredComponent { name } => write!(f, "unregistered component '{name}'"),
51            Self::WrongStorageKind { name } => write!(f, "wrong storage kind for '{name}'"),
52            Self::ConflictingFilters { detail } => write!(f, "conflicting filters: {detail}"),
53            Self::DuplicateMutableComponent { name } => {
54                write!(f, "duplicate mutable component '{name}'")
55            }
56            Self::WrongOwner => f.write_str("query handle belongs to another world"),
57            Self::StaleCache => f.write_str("stale query cache handle"),
58            Self::WrongQuery { detail } => write!(f, "wrong query cursor: {detail}"),
59            Self::MovingChangeWindow => f.write_str(
60                "added/changed filters require a traversal or membership policy, not Result",
61            ),
62            Self::UnsupportedCachePolicy { detail } => {
63                write!(f, "unsupported cache policy: {detail}")
64            }
65            Self::ExactIdOrderConflict => f.write_str("exact-id order conflicts with result cache"),
66            Self::DuplicateExactId { entity } => {
67                write!(f, "exact-id query contains duplicate entity {entity:?}")
68            }
69            Self::MissingExactId { entity } => {
70                write!(f, "exact-id query missing unavailable entity {entity:?}")
71            }
72            Self::BorrowConflict { detail } => write!(f, "query borrow conflict: {detail}"),
73            Self::CommandRejected { detail } => write!(f, "query command rejected: {detail}"),
74            Self::OwnerMismatch => f.write_str("query handle owner mismatch"),
75            Self::TraversalAborted { entity, detail } => {
76                write!(f, "query traversal aborted at {entity:?}: {detail}")
77            }
78        }
79    }
80}
81
82#[cfg(feature = "std")]
83impl std::error::Error for QueryError {}
84
85#[cfg(all(test, feature = "std"))]
86mod tests {
87    use super::*;
88    use crate::component::ComponentOptions;
89    use crate::world::WorldBuilder;
90    use alloc::string::ToString;
91
92    #[test]
93    fn display_covers_entity_and_command_diagnostics() {
94        let mut builder = WorldBuilder::new();
95        builder
96            .register_component::<u8>(ComponentOptions::sparse())
97            .expect("component");
98        let mut world = builder.build().expect("world");
99        let entity = world.spawn().expect("entity");
100
101        assert!(QueryError::DuplicateExactId { entity }
102            .to_string()
103            .contains("duplicate entity"));
104        assert_eq!(
105            QueryError::CommandRejected {
106                detail: String::from("stale target"),
107            }
108            .to_string(),
109            "query command rejected: stale target"
110        );
111    }
112}