1use std::error::Error;
2use std::fmt;
3
4use frame_core::capability::{CapabilityCheckError, CapabilityDenied};
5use frame_core::component::ComponentId;
6use haematite::{
7 BranchCommitError, BranchError, BranchKind, BranchPolicyError, BranchRefError, CheckoutError,
8 MergeError, NodeError, SnapshotError, StoreError, TreeError,
9};
10
11use crate::types::{MergePolicy, ReferenceTargetState};
12
13#[derive(Debug)]
15pub enum ReconcileInconsistency {
16 BranchWithoutMeta {
18 branch: String,
20 kind: BranchKind,
22 },
23 ActiveWithoutBranch {
25 component: ComponentId,
27 },
28 ArchivedWithBranch {
30 component: ComponentId,
32 },
33 RemovedWithoutSnapshot {
35 component: ComponentId,
37 snapshot: String,
39 },
40 SnapshotRootMismatch {
42 snapshot: String,
44 expected: haematite::Hash,
46 actual: haematite::Hash,
48 },
49}
50
51impl fmt::Display for ReconcileInconsistency {
52 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Self::BranchWithoutMeta { branch, kind } => write!(
55 formatter,
56 "durable branch '{branch}' ({kind}) has no frame-state meta record"
57 ),
58 Self::ActiveWithoutBranch { component } => {
59 write!(
60 formatter,
61 "active component {component} has no Namespace branch"
62 )
63 }
64 Self::ArchivedWithBranch { component } => write!(
65 formatter,
66 "archived component {component} still has a Namespace branch"
67 ),
68 Self::RemovedWithoutSnapshot {
69 component,
70 snapshot,
71 } => write!(
72 formatter,
73 "component {component} branch was removed without required snapshot '{snapshot}'"
74 ),
75 Self::SnapshotRootMismatch {
76 snapshot,
77 expected,
78 actual,
79 } => write!(
80 formatter,
81 "snapshot '{snapshot}' names root {actual}, expected {expected}"
82 ),
83 }
84 }
85}
86
87impl Error for ReconcileInconsistency {}
88
89#[derive(Debug)]
91pub enum StateError {
92 Io(std::io::Error),
94 Store(StoreError),
96 Node(NodeError),
98 Tree(TreeError),
100 BranchCommit(BranchCommitError),
102 BranchPolicy(BranchPolicyError),
104 Merge(MergeError),
106 BranchRef(BranchRefError),
108 Branch(BranchError),
110 Snapshot(SnapshotError),
112 Checkout(CheckoutError),
114 CapabilityBinding {
116 handle_component: ComponentId,
118 checker_component: ComponentId,
120 },
121 CapabilityDenied(CapabilityDenied),
123 CapabilityCheck(CapabilityCheckError),
125 DanglingReference {
127 target: ComponentId,
129 state: ReferenceTargetState,
131 },
132 SynchronizationPoisoned,
134 SchemaNotDeclared {
136 component: ComponentId,
138 },
139 SchemaAlreadyDeclared {
141 component: ComponentId,
143 },
144 UnsupportedPolicy {
146 policy: MergePolicy,
148 },
149 ResolverNotRegistered {
151 name: String,
153 },
154 ResolverAlreadyRegistered {
156 name: String,
158 },
159 InstallInProgress {
161 component: ComponentId,
163 },
164 ArchiveInProgress {
166 component: ComponentId,
168 },
169 WorkInProgress {
171 component: ComponentId,
173 },
174 AlreadyActive {
176 component: ComponentId,
178 },
179 NotActive {
181 component: ComponentId,
183 },
184 StaleHandle {
186 component: ComponentId,
188 held: u64,
190 current: u64,
192 },
193 WorkNotFound {
195 branch: String,
197 },
198 ForeignWorkHandle,
200 CorruptRecord {
202 detail: String,
204 },
205 Reconcile(ReconcileInconsistency),
207}
208
209impl fmt::Display for StateError {
210 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 Self::Io(error) => write!(formatter, "frame-state filesystem error: {error}"),
213 Self::Store(error) => write!(formatter, "frame-state node store error: {error}"),
214 Self::Node(error) => write!(formatter, "frame-state node encoding error: {error}"),
215 Self::Tree(error) => write!(formatter, "frame-state tree traversal error: {error}"),
216 Self::BranchCommit(error) => error.fmt(formatter),
217 Self::BranchPolicy(error) => error.fmt(formatter),
218 Self::Merge(error) => error.fmt(formatter),
219 Self::BranchRef(error) => error.fmt(formatter),
220 Self::Branch(error) => error.fmt(formatter),
221 Self::Snapshot(error) => error.fmt(formatter),
222 Self::Checkout(error) => error.fmt(formatter),
223 Self::CapabilityBinding {
224 handle_component,
225 checker_component,
226 } => fmt_capability_binding(formatter, *handle_component, *checker_component),
227 Self::CapabilityDenied(denial) => fmt_capability_denied(formatter, denial),
228 Self::CapabilityCheck(error) => error.fmt(formatter),
229 Self::DanglingReference { target, state } => {
230 fmt_dangling_reference(formatter, *target, *state)
231 }
232 Self::SynchronizationPoisoned => {
233 formatter.write_str("frame-state synchronization poisoned")
234 }
235 Self::SchemaNotDeclared { component } => {
236 write!(
237 formatter,
238 "component {component} has no declared storage schema"
239 )
240 }
241 Self::SchemaAlreadyDeclared { component } => {
242 write!(
243 formatter,
244 "component {component} storage schema is already declared"
245 )
246 }
247 Self::UnsupportedPolicy { policy } => {
248 write!(
249 formatter,
250 "haematite 0.5.0 cannot execute merge policy {policy}"
251 )
252 }
253 Self::ResolverNotRegistered { name } => {
254 write!(
255 formatter,
256 "custom merge resolver '{name}' is not registered"
257 )
258 }
259 Self::ResolverAlreadyRegistered { name } => {
260 write!(
261 formatter,
262 "custom merge resolver '{name}' is already registered"
263 )
264 }
265 Self::InstallInProgress { component } => {
266 write!(
267 formatter,
268 "component {component} storage install is in progress"
269 )
270 }
271 Self::ArchiveInProgress { component } => {
272 write!(
273 formatter,
274 "component {component} storage archive is in progress"
275 )
276 }
277 Self::WorkInProgress { component } => {
278 write!(
279 formatter,
280 "component {component} Work operation is in progress"
281 )
282 }
283 Self::AlreadyActive { component } => {
284 write!(formatter, "component {component} storage is already active")
285 }
286 Self::NotActive { component } => {
287 write!(formatter, "component {component} storage is not active")
288 }
289 Self::StaleHandle {
290 component,
291 held,
292 current,
293 } => write!(
294 formatter,
295 "stale handle for component {component}: held incarnation {held}, current incarnation {current}"
296 ),
297 Self::WorkNotFound { branch } => {
298 write!(formatter, "Work branch '{branch}' is not outstanding")
299 }
300 Self::ForeignWorkHandle => formatter
301 .write_str("Work handle belongs to another store, component, or incarnation"),
302 Self::CorruptRecord { detail } => {
303 write!(formatter, "corrupt frame-state record: {detail}")
304 }
305 Self::Reconcile(error) => error.fmt(formatter),
306 }
307 }
308}
309
310fn fmt_capability_binding(
311 formatter: &mut fmt::Formatter<'_>,
312 handle_component: ComponentId,
313 checker_component: ComponentId,
314) -> fmt::Result {
315 write!(
316 formatter,
317 "capability checker for component {checker_component} cannot resolve through component {handle_component}'s storage handle"
318 )
319}
320
321fn fmt_capability_denied(
322 formatter: &mut fmt::Formatter<'_>,
323 denial: &CapabilityDenied,
324) -> fmt::Result {
325 write!(
326 formatter,
327 "cross-component resolution denied for component {}: kind {:?}, scope {:?}, declared {}",
328 denial.component_id, denial.kind, denial.scope, denial.declared
329 )
330}
331
332fn fmt_dangling_reference(
333 formatter: &mut fmt::Formatter<'_>,
334 target: ComponentId,
335 state: ReferenceTargetState,
336) -> fmt::Result {
337 write!(
338 formatter,
339 "cross-component reference target {target} is {state}"
340 )
341}
342
343impl Error for StateError {
344 fn source(&self) -> Option<&(dyn Error + 'static)> {
345 match self {
346 Self::Io(error) => Some(error),
347 Self::Store(error) => Some(error),
348 Self::Node(error) => Some(error),
349 Self::Tree(error) => Some(error),
350 Self::BranchCommit(error) => Some(error),
351 Self::BranchPolicy(error) => Some(error),
352 Self::Merge(error) => Some(error),
353 Self::BranchRef(error) => Some(error),
354 Self::Branch(error) => Some(error),
355 Self::Snapshot(error) => Some(error),
356 Self::Checkout(error) => Some(error),
357 Self::CapabilityCheck(error) => Some(error),
358 Self::Reconcile(error) => Some(error),
359 _ => None,
360 }
361 }
362}
363
364impl From<std::io::Error> for StateError {
365 fn from(error: std::io::Error) -> Self {
366 Self::Io(error)
367 }
368}
369impl From<StoreError> for StateError {
370 fn from(error: StoreError) -> Self {
371 Self::Store(error)
372 }
373}
374impl From<NodeError> for StateError {
375 fn from(error: NodeError) -> Self {
376 Self::Node(error)
377 }
378}
379impl From<TreeError> for StateError {
380 fn from(error: TreeError) -> Self {
381 Self::Tree(error)
382 }
383}
384impl From<BranchRefError> for StateError {
385 fn from(error: BranchRefError) -> Self {
386 Self::BranchRef(error)
387 }
388}
389impl From<BranchError> for StateError {
390 fn from(error: BranchError) -> Self {
391 Self::Branch(error)
392 }
393}
394impl From<SnapshotError> for StateError {
395 fn from(error: SnapshotError) -> Self {
396 Self::Snapshot(error)
397 }
398}
399impl From<CheckoutError> for StateError {
400 fn from(error: CheckoutError) -> Self {
401 Self::Checkout(error)
402 }
403}
404impl From<CapabilityCheckError> for StateError {
405 fn from(error: CapabilityCheckError) -> Self {
406 Self::CapabilityCheck(error)
407 }
408}
409impl From<ReconcileInconsistency> for StateError {
410 fn from(error: ReconcileInconsistency) -> Self {
411 Self::Reconcile(error)
412 }
413}
414
415impl From<BranchCommitError> for StateError {
416 fn from(error: BranchCommitError) -> Self {
417 match error {
418 BranchCommitError::Policy(policy) => Self::BranchPolicy(policy),
419 BranchCommitError::Merge(merge) => Self::Merge(merge),
420 other => Self::BranchCommit(other),
421 }
422 }
423}