use std::fmt;
use crate::engine::types::ComponentID;
#[cfg(feature = "gpu")]
use crate::engine::types::ArchetypeID;
#[cfg(feature = "gpu")]
use crate::engine::types::GPUAccessMode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessKind {
Read,
Write,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InvalidAccessReason {
ReadAndWrite,
DuplicateAccess,
WriteAndWithout,
ReadAndWithout,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoundaryAccessFailure {
OutOfRange,
TypeMismatch,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutionError {
StructuralMutationDuringIteration,
BorrowConflict {
component_id: ComponentID,
held: AccessKind,
requested: AccessKind,
},
InvalidQueryAccess {
component_id: ComponentID,
reason: InvalidAccessReason,
},
SelfChannelAlias {
channel_id: crate::engine::types::ChannelID,
},
BoundaryAccessFailed {
reason: BoundaryAccessFailure,
id: crate::engine::types::BoundaryID,
},
DuplicateChannelRegistration {
channel_id: crate::engine::types::ChannelID,
existing_boundary: crate::engine::types::BoundaryID,
},
ChannelIdOverflow,
MissingComponent {
component_id: ComponentID,
},
QueryTypeMismatch {
method: &'static str,
access: AccessKind,
index: usize,
component_id: ComponentID,
expected: &'static str,
actual: &'static str,
},
DuplicateSystemId {
system_id: crate::engine::types::SystemID,
},
UnknownSystemId {
system_id: crate::engine::types::SystemID,
},
SelfSystemOrdering {
system_id: crate::engine::types::SystemID,
},
SchedulerInvariantViolation,
SchedulerCycle,
LockPoisoned {
what: &'static str,
},
GpuNotEnabled,
#[cfg(feature = "gpu")]
GpuUnsupportedComponent {
component_id: ComponentID,
name: &'static str,
},
#[cfg(feature = "gpu")]
GpuInitFailed {
message: std::borrow::Cow<'static, str>,
},
#[cfg(feature = "gpu")]
GpuDispatchFailed {
message: std::borrow::Cow<'static, str>,
},
#[cfg(feature = "gpu")]
GpuMissingBuffer {
archetype_id: ArchetypeID,
component_id: ComponentID,
access: GPUAccessMode,
},
InternalExecutionError,
SystemRuntime(String),
}
impl fmt::Display for ExecutionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExecutionError::StructuralMutationDuringIteration =>
f.write_str("structural mutation attempted during ECS iteration"),
ExecutionError::BorrowConflict { component_id, held, requested } =>
write!(f,
"borrow conflict on component {}: {:?} already held, {:?} requested",
component_id, held, requested),
ExecutionError::InvalidQueryAccess { component_id, reason } =>
write!(f, "invalid query access for component {}: {:?}",
component_id, reason),
ExecutionError::SelfChannelAlias { channel_id } => {
write!(
f,
"system access sets alias channel {}: it appears in both produces and consumes",
channel_id
)
}
ExecutionError::BoundaryAccessFailed { reason, id } => {
match reason {
BoundaryAccessFailure::OutOfRange => write!(
f,
"boundary access failed: BoundaryID {} is out of range",
id
),
BoundaryAccessFailure::TypeMismatch => write!(
f,
"boundary access failed: stored resource at BoundaryID {} has a different concrete type than requested",
id
),
}
}
ExecutionError::DuplicateChannelRegistration { channel_id, existing_boundary } => {
write!(
f,
"boundary registration failed: channel {} is already owned by BoundaryID {}",
channel_id, existing_boundary
)
}
ExecutionError::ChannelIdOverflow =>
f.write_str("channel ID allocation overflowed"),
ExecutionError::MissingComponent { component_id } =>
write!(f, "query attempted to access missing component {}", component_id),
ExecutionError::QueryTypeMismatch {
method,
access,
index,
component_id,
expected,
actual,
} => write!(
f,
"{method}: {:?} column {index} for component {component_id} was built for {expected}, got {actual}",
access
),
ExecutionError::DuplicateSystemId { system_id } =>
write!(f, "scheduler registration failed: duplicate system id {}", system_id),
ExecutionError::UnknownSystemId { system_id } =>
write!(f, "scheduler ordering references unknown system id {}", system_id),
ExecutionError::SelfSystemOrdering { system_id } =>
write!(f, "scheduler ordering cannot make system {} depend on itself", system_id),
ExecutionError::SchedulerInvariantViolation =>
f.write_str("scheduler violated declared access invariants"),
ExecutionError::SchedulerCycle =>
f.write_str(
"scheduler detected a dependency cycle in the system graph; \
check AccessSets::produces/consumes and explicit ordering edges"
),
ExecutionError::LockPoisoned { what } =>
write!(f, "lock poisoned: {}", what),
ExecutionError::GpuNotEnabled =>
f.write_str("GPU execution requested but the `gpu` feature has not been enabled"),
#[cfg(feature = "gpu")]
ExecutionError::GpuUnsupportedComponent { component_id, name } =>
write!(f,
"component {} ({}) is not GPU-safe (register_gpu_component required)",
component_id, name),
#[cfg(feature = "gpu")]
ExecutionError::GpuInitFailed { message } =>
write!(f, "GPU initialization failed: {}", message),
#[cfg(feature = "gpu")]
ExecutionError::GpuDispatchFailed { message } =>
write!(f, "GPU dispatch failed: {}", message),
#[cfg(feature = "gpu")]
ExecutionError::GpuMissingBuffer { archetype_id, component_id, access } =>
write!(f,
"GPU dispatch failed: missing {:?} buffer for component {} in archetype {}",
access, component_id, archetype_id),
ExecutionError::InternalExecutionError =>
f.write_str("internal ECS execution error"),
ExecutionError::SystemRuntime(msg) =>
write!(f, "system runtime error: {}", msg),
}
}
}
impl std::error::Error for ExecutionError {}