Skip to main content

qubit_batch/execute/
batch_execution_error.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10use thiserror::Error;
11
12use crate::BatchOutcome;
13
14/// Batch-level error returned when the batch contract is violated.
15///
16/// Task failures are reported through [`BatchOutcome`], not through
17/// this enum. This error is reserved for situations such as declared task-count
18/// mismatches.
19///
20/// ```rust
21/// use qubit_batch::{
22///     BatchExecutionError,
23///     BatchExecutor,
24///     SequentialBatchExecutor,
25/// };
26///
27/// let error = SequentialBatchExecutor::new()
28///     .for_each_with_count([10, 20], 3, |_value| Ok::<(), &'static str>(()))
29///     .expect_err("iterator should yield fewer items than declared");
30///
31/// assert!(error.is_count_shortfall());
32/// assert_eq!(error.outcome().completed_count(), 2);
33/// match error {
34///     BatchExecutionError::CountShortfall { expected, actual, .. } => {
35///         assert_eq!(expected, 3);
36///         assert_eq!(actual, 2);
37///     }
38///     BatchExecutionError::CountExceeded { .. } => unreachable!(),
39/// }
40/// ```
41///
42/// # Type Parameters
43///
44/// * `E` - The task-specific error type stored inside the attached outcome.
45///
46#[derive(Debug, Clone, Error, PartialEq, Eq)]
47pub enum BatchExecutionError<E> {
48    /// The task source ended before the declared task count was reached.
49    #[error("batch task count shortfall: expected {expected}, actual {actual}")]
50    CountShortfall {
51        /// Declared task count.
52        expected: usize,
53        /// Actual number of tasks observed from the source.
54        actual: usize,
55        /// Outcome accumulated from the tasks that did run.
56        outcome: BatchOutcome<E>,
57    },
58
59    /// The task source yielded more tasks than the declared task count.
60    #[error(
61        "batch task count exceeded: expected {expected}, observed at least {observed_at_least}"
62    )]
63    CountExceeded {
64        /// Declared task count.
65        expected: usize,
66        /// Lower bound of observed tasks. This is typically `expected + 1`
67        /// because the executor stops reading once it confirms the overflow.
68        observed_at_least: usize,
69        /// Outcome accumulated from the tasks that did run.
70        outcome: BatchOutcome<E>,
71    },
72}
73
74impl<E> BatchExecutionError<E> {
75    /// Returns the batch outcome attached to this error.
76    ///
77    /// # Returns
78    ///
79    /// A shared reference to the attached batch outcome.
80    #[inline]
81    pub const fn outcome(&self) -> &BatchOutcome<E> {
82        match self {
83            Self::CountShortfall { outcome, .. } | Self::CountExceeded { outcome, .. } => outcome,
84        }
85    }
86
87    /// Consumes this error and returns the attached batch outcome.
88    ///
89    /// # Returns
90    ///
91    /// The batch outcome accumulated before this error was reported.
92    #[inline]
93    pub fn into_outcome(self) -> BatchOutcome<E> {
94        match self {
95            Self::CountShortfall { outcome, .. } | Self::CountExceeded { outcome, .. } => outcome,
96        }
97    }
98
99    /// Returns whether this error represents a task-count shortfall.
100    ///
101    /// # Returns
102    ///
103    /// `true` if this error is [`Self::CountShortfall`].
104    #[inline]
105    pub const fn is_count_shortfall(&self) -> bool {
106        matches!(self, Self::CountShortfall { .. })
107    }
108
109    /// Returns whether this error represents an oversized task source.
110    ///
111    /// # Returns
112    ///
113    /// `true` if this error is [`Self::CountExceeded`].
114    #[inline]
115    pub const fn is_count_exceeded(&self) -> bool {
116        matches!(self, Self::CountExceeded { .. })
117    }
118}