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