use crate::{DeriveError, NodeId, OutputKey, ScopeId, TransactionId};
use core::fmt;
pub type GraphResult<T> = Result<T, GraphError>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ErrorCategory {
ProgrammerError,
DeriveError,
PlanError,
OutputError,
HostResourceStatus,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ErrorAuditEvent {
pub category: ErrorCategory,
pub target: ErrorTarget,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ErrorTarget {
Graph,
Node(NodeId),
Scope(ScopeId),
Transaction(TransactionId),
Output(OutputKey),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PlanError {
Message(String),
}
impl PlanError {
pub fn message(message: impl Into<String>) -> Self {
Self::Message(message.into())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OutputError {
Read(DeriveError),
Message(String),
}
impl OutputError {
pub fn message(message: impl Into<String>) -> Self {
Self::Message(message.into())
}
}
impl From<DeriveError> for OutputError {
fn from(error: DeriveError) -> Self {
Self::Read(error)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum GraphError {
UnknownNode(NodeId),
UnknownScope(ScopeId),
DuplicateDependency(NodeId),
SelfDependency(NodeId),
NodeAlreadyAttached(NodeId),
ScopeAlreadyClosed(ScopeId),
ScopeClosed(ScopeId),
NestedTransaction,
TransactionClosed(TransactionId),
NotInputNode(NodeId),
NotDerivedNode(NodeId),
NotCollectionNode(NodeId),
WrongInputType(NodeId),
WrongDerivedType(NodeId),
WrongCollectionType(NodeId),
UnknownOutput(OutputKey),
OutputFailed(OutputKey, OutputError),
PlanFailed(ScopeId, PlanError),
ResourceScopeMismatch(ScopeId),
ResourceNotOwned,
CycleDetected(NodeId),
CollectionDependencyNotAllowed(NodeId),
DeriveFailed(NodeId, DeriveError),
CollectionFailed(NodeId, DeriveError),
FullRecomputeMismatch(NodeId),
}
impl fmt::Display for GraphError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnknownNode(id) => write!(f, "unknown node: {id:?}"),
Self::UnknownScope(id) => write!(f, "unknown scope: {id:?}"),
Self::DuplicateDependency(id) => write!(f, "duplicate dependency: {id:?}"),
Self::SelfDependency(id) => write!(f, "self dependency: {id:?}"),
Self::NodeAlreadyAttached(id) => write!(f, "node already attached: {id:?}"),
Self::ScopeAlreadyClosed(id) => write!(f, "scope already closed: {id:?}"),
Self::ScopeClosed(id) => write!(f, "scope already closed: {id:?}"),
Self::NestedTransaction => write!(f, "a transaction is already open"),
Self::TransactionClosed(id) => write!(f, "transaction already closed: {id:?}"),
Self::NotInputNode(id) => write!(f, "node is not an input: {id:?}"),
Self::NotDerivedNode(id) => write!(f, "node is not derived: {id:?}"),
Self::NotCollectionNode(id) => write!(f, "node is not a collection: {id:?}"),
Self::WrongInputType(id) => write!(f, "wrong input value type for node: {id:?}"),
Self::WrongDerivedType(id) => write!(f, "wrong derived value type for node: {id:?}"),
Self::WrongCollectionType(id) => {
write!(f, "wrong collection value type for node: {id:?}")
}
Self::UnknownOutput(key) => write!(f, "unknown output: {key:?}"),
Self::OutputFailed(key, error) => write!(f, "output failed for {key:?}: {error:?}"),
Self::PlanFailed(scope, error) => {
write!(f, "resource planner failed for {scope:?}: {error:?}")
}
Self::ResourceScopeMismatch(id) => write!(f, "resource scope mismatch: {id:?}"),
Self::ResourceNotOwned => write!(f, "resource is not owned"),
Self::CycleDetected(id) => write!(f, "dependency cycle detected at node: {id:?}"),
Self::CollectionDependencyNotAllowed(id) => {
write!(
f,
"collection dependency is not allowed for derived node: {id:?}"
)
}
Self::DeriveFailed(id, error) => write!(f, "derive failed for {id:?}: {error:?}"),
Self::CollectionFailed(id, error) => {
write!(f, "collection failed for {id:?}: {error:?}")
}
Self::FullRecomputeMismatch(id) => {
write!(f, "full recompute mismatch for node: {id:?}")
}
}
}
}
impl GraphError {
pub const fn category(&self) -> ErrorCategory {
match self {
Self::DeriveFailed(_, _) | Self::CollectionFailed(_, _) => ErrorCategory::DeriveError,
Self::PlanFailed(_, _) => ErrorCategory::PlanError,
Self::OutputFailed(_, _) => ErrorCategory::OutputError,
_ => ErrorCategory::ProgrammerError,
}
}
pub const fn audit_event(&self) -> ErrorAuditEvent {
ErrorAuditEvent {
category: self.category(),
target: match self {
Self::UnknownNode(node)
| Self::DuplicateDependency(node)
| Self::SelfDependency(node)
| Self::NodeAlreadyAttached(node)
| Self::NotInputNode(node)
| Self::NotDerivedNode(node)
| Self::NotCollectionNode(node)
| Self::WrongInputType(node)
| Self::WrongDerivedType(node)
| Self::WrongCollectionType(node)
| Self::CycleDetected(node)
| Self::CollectionDependencyNotAllowed(node)
| Self::DeriveFailed(node, _)
| Self::CollectionFailed(node, _)
| Self::FullRecomputeMismatch(node) => ErrorTarget::Node(*node),
Self::UnknownScope(scope)
| Self::ScopeAlreadyClosed(scope)
| Self::ScopeClosed(scope)
| Self::ResourceScopeMismatch(scope)
| Self::PlanFailed(scope, _) => ErrorTarget::Scope(*scope),
Self::TransactionClosed(transaction) => ErrorTarget::Transaction(*transaction),
Self::UnknownOutput(output) | Self::OutputFailed(output, _) => {
ErrorTarget::Output(*output)
}
Self::NestedTransaction | Self::ResourceNotOwned => ErrorTarget::Graph,
},
}
}
}
impl std::error::Error for GraphError {}