Skip to main content

qubit_progress/error/
recoverable_finish_error.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Errors returned by recoverable checked successful completion.
9// qubit-style: allow source-test-pair
10
11use std::{
12    error::Error,
13    fmt,
14};
15
16use crate::{
17    Progress,
18    error::{
19        CompletionError,
20        TerminalError,
21    },
22};
23
24/// Failure from checked finish while preserving a reusable progress operation.
25#[allow(clippy::large_enum_variant)]
26pub enum RecoverableFinishError<'reporter> {
27    /// Completion validation failed and the operation remains reusable.
28    Incomplete {
29        /// Progress operation returned to the caller.
30        progress: Progress<'reporter>,
31        /// First completion invariant that failed.
32        source: CompletionError,
33    },
34    /// A terminal emission was attempted and failed permanently.
35    Terminal(TerminalError),
36}
37
38impl<'reporter> RecoverableFinishError<'reporter> {
39    /// Returns the completion error when validation failed.
40    #[must_use]
41    pub fn completion_error(&self) -> Option<&CompletionError> {
42        match self {
43            Self::Incomplete { source, .. } => Some(source),
44            Self::Terminal(_) => None,
45        }
46    }
47
48    /// Consumes an incomplete finish error and returns the reusable Progress.
49    pub fn into_progress(self) -> Result<Progress<'reporter>, TerminalError> {
50        match self {
51            Self::Incomplete { progress, .. } => Ok(progress),
52            Self::Terminal(error) => Err(error),
53        }
54    }
55
56    /// Consumes this error and returns its recoverable or terminal parts.
57    pub fn into_parts(
58        self,
59    ) -> Result<(Progress<'reporter>, CompletionError), TerminalError> {
60        match self {
61            Self::Incomplete { progress, source } => Ok((progress, source)),
62            Self::Terminal(error) => Err(error),
63        }
64    }
65}
66
67impl fmt::Debug for RecoverableFinishError<'_> {
68    /// Formats the error without requiring the reporter to implement Debug.
69    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70        match self {
71            Self::Incomplete { source, .. } => formatter
72                .debug_struct("RecoverableFinishError::Incomplete")
73                .field("source", source)
74                .finish(),
75            Self::Terminal(error) => formatter
76                .debug_tuple("RecoverableFinishError::Terminal")
77                .field(error)
78                .finish(),
79        }
80    }
81}
82
83impl fmt::Display for RecoverableFinishError<'_> {
84    /// Formats the completion or terminal failure.
85    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self {
87            Self::Incomplete { source, .. } => source.fmt(formatter),
88            Self::Terminal(error) => error.fmt(formatter),
89        }
90    }
91}
92
93impl Error for RecoverableFinishError<'_> {
94    /// Returns the nested completion or terminal error.
95    fn source(&self) -> Option<&(dyn Error + 'static)> {
96        match self {
97            Self::Incomplete { source, .. } => Some(source),
98            Self::Terminal(error) => Some(error),
99        }
100    }
101}