1use crate::{DeriveError, NodeId, OutputKey, ScopeId, TransactionId};
2use core::fmt;
3
4pub type GraphResult<T> = Result<T, GraphError>;
6
7#[derive(Copy, Clone, Debug, Eq, PartialEq)]
9pub enum ErrorCategory {
10 ProgrammerError,
12 DeriveError,
14 PlanError,
16 OutputError,
18 HostResourceStatus,
20}
21
22#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct ErrorAuditEvent {
25 pub category: ErrorCategory,
27 pub target: ErrorTarget,
29}
30
31#[derive(Copy, Clone, Debug, Eq, PartialEq)]
33pub enum ErrorTarget {
34 Graph,
36 Node(NodeId),
38 Scope(ScopeId),
40 Transaction(TransactionId),
42 Output(OutputKey),
44}
45
46#[derive(Clone, Debug, Eq, PartialEq)]
48pub enum PlanError {
49 Message(String),
51}
52
53impl PlanError {
54 pub fn message(message: impl Into<String>) -> Self {
56 Self::Message(message.into())
57 }
58}
59
60#[derive(Clone, Debug, Eq, PartialEq)]
62pub enum OutputError {
63 Read(DeriveError),
65 Message(String),
67}
68
69impl OutputError {
70 pub fn message(message: impl Into<String>) -> Self {
72 Self::Message(message.into())
73 }
74}
75
76impl From<DeriveError> for OutputError {
77 fn from(error: DeriveError) -> Self {
78 Self::Read(error)
79 }
80}
81
82#[derive(Clone, Debug, Eq, PartialEq)]
84pub enum GraphError {
85 UnknownNode(NodeId),
87 UnknownScope(ScopeId),
89 DuplicateDependency(NodeId),
91 SelfDependency(NodeId),
93 NodeAlreadyAttached(NodeId),
95 ScopeAlreadyClosed(ScopeId),
97 ScopeClosed(ScopeId),
99 NestedTransaction,
101 TransactionClosed(TransactionId),
103 NotInputNode(NodeId),
105 NotDerivedNode(NodeId),
107 NotCollectionNode(NodeId),
109 WrongInputType(NodeId),
111 WrongDerivedType(NodeId),
113 WrongCollectionType(NodeId),
115 UnknownOutput(OutputKey),
117 OutputFailed(OutputKey, OutputError),
119 PlanFailed(ScopeId, PlanError),
121 ResourceScopeMismatch(ScopeId),
123 ResourceNotOwned,
125 CycleDetected(NodeId),
127 CollectionDependencyNotAllowed(NodeId),
129 DeriveFailed(NodeId, DeriveError),
131 CollectionFailed(NodeId, DeriveError),
133 FullRecomputeMismatch(NodeId),
135}
136
137impl fmt::Display for GraphError {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 match self {
140 Self::UnknownNode(id) => write!(f, "unknown node: {id:?}"),
141 Self::UnknownScope(id) => write!(f, "unknown scope: {id:?}"),
142 Self::DuplicateDependency(id) => write!(f, "duplicate dependency: {id:?}"),
143 Self::SelfDependency(id) => write!(f, "self dependency: {id:?}"),
144 Self::NodeAlreadyAttached(id) => write!(f, "node already attached: {id:?}"),
145 Self::ScopeAlreadyClosed(id) => write!(f, "scope already closed: {id:?}"),
146 Self::ScopeClosed(id) => write!(f, "scope already closed: {id:?}"),
147 Self::NestedTransaction => write!(f, "a transaction is already open"),
148 Self::TransactionClosed(id) => write!(f, "transaction already closed: {id:?}"),
149 Self::NotInputNode(id) => write!(f, "node is not an input: {id:?}"),
150 Self::NotDerivedNode(id) => write!(f, "node is not derived: {id:?}"),
151 Self::NotCollectionNode(id) => write!(f, "node is not a collection: {id:?}"),
152 Self::WrongInputType(id) => write!(f, "wrong input value type for node: {id:?}"),
153 Self::WrongDerivedType(id) => write!(f, "wrong derived value type for node: {id:?}"),
154 Self::WrongCollectionType(id) => {
155 write!(f, "wrong collection value type for node: {id:?}")
156 }
157 Self::UnknownOutput(key) => write!(f, "unknown output: {key:?}"),
158 Self::OutputFailed(key, error) => write!(f, "output failed for {key:?}: {error:?}"),
159 Self::PlanFailed(scope, error) => {
160 write!(f, "resource planner failed for {scope:?}: {error:?}")
161 }
162 Self::ResourceScopeMismatch(id) => write!(f, "resource scope mismatch: {id:?}"),
163 Self::ResourceNotOwned => write!(f, "resource is not owned"),
164 Self::CycleDetected(id) => write!(f, "dependency cycle detected at node: {id:?}"),
165 Self::CollectionDependencyNotAllowed(id) => {
166 write!(
167 f,
168 "collection dependency is not allowed for derived node: {id:?}"
169 )
170 }
171 Self::DeriveFailed(id, error) => write!(f, "derive failed for {id:?}: {error:?}"),
172 Self::CollectionFailed(id, error) => {
173 write!(f, "collection failed for {id:?}: {error:?}")
174 }
175 Self::FullRecomputeMismatch(id) => {
176 write!(f, "full recompute mismatch for node: {id:?}")
177 }
178 }
179 }
180}
181
182impl GraphError {
183 pub const fn category(&self) -> ErrorCategory {
185 match self {
186 Self::DeriveFailed(_, _) | Self::CollectionFailed(_, _) => ErrorCategory::DeriveError,
187 Self::PlanFailed(_, _) => ErrorCategory::PlanError,
188 Self::OutputFailed(_, _) => ErrorCategory::OutputError,
189 _ => ErrorCategory::ProgrammerError,
190 }
191 }
192
193 pub const fn audit_event(&self) -> ErrorAuditEvent {
195 ErrorAuditEvent {
196 category: self.category(),
197 target: match self {
198 Self::UnknownNode(node)
199 | Self::DuplicateDependency(node)
200 | Self::SelfDependency(node)
201 | Self::NodeAlreadyAttached(node)
202 | Self::NotInputNode(node)
203 | Self::NotDerivedNode(node)
204 | Self::NotCollectionNode(node)
205 | Self::WrongInputType(node)
206 | Self::WrongDerivedType(node)
207 | Self::WrongCollectionType(node)
208 | Self::CycleDetected(node)
209 | Self::CollectionDependencyNotAllowed(node)
210 | Self::DeriveFailed(node, _)
211 | Self::CollectionFailed(node, _)
212 | Self::FullRecomputeMismatch(node) => ErrorTarget::Node(*node),
213 Self::UnknownScope(scope)
214 | Self::ScopeAlreadyClosed(scope)
215 | Self::ScopeClosed(scope)
216 | Self::ResourceScopeMismatch(scope)
217 | Self::PlanFailed(scope, _) => ErrorTarget::Scope(*scope),
218 Self::TransactionClosed(transaction) => ErrorTarget::Transaction(*transaction),
219 Self::UnknownOutput(output) | Self::OutputFailed(output, _) => {
220 ErrorTarget::Output(*output)
221 }
222 Self::NestedTransaction | Self::ResourceNotOwned => ErrorTarget::Graph,
223 },
224 }
225 }
226}
227
228impl std::error::Error for GraphError {}