Skip to main content

qubit_codec/buffered/
finish_error.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Error reported while finishing buffered output.
11
12use thiserror::Error;
13
14use super::capacity_error::CapacityError;
15
16/// Error reported by one-shot buffered finalization.
17///
18/// `finish` methods require enough output capacity to write all final output in
19/// one call. This type separates caller capacity mistakes from semantic errors
20/// reported by the concrete codec or hook policy.
21#[derive(Clone, Copy, Debug, Eq, Error, Hash, PartialEq)]
22pub enum FinishError<E> {
23    /// Finish-output bound arithmetic overflowed.
24    #[error("finish output capacity planning failed: {source}")]
25    Capacity {
26        /// Capacity planning error.
27        #[source]
28        source: CapacityError,
29    },
30
31    /// The caller supplied an output index outside the output slice.
32    #[error("invalid finish output index {index} for output length {len}")]
33    InvalidOutputIndex {
34        /// Invalid output index supplied by the caller.
35        index: usize,
36        /// Length of the output slice.
37        len: usize,
38    },
39
40    /// The output slice cannot hold all final output.
41    #[error("insufficient finish output at index {output_index}: required {required} units, available {available}")]
42    InsufficientOutput {
43        /// Absolute output index where finalization would start writing.
44        output_index: usize,
45        /// Output units required to finish in one call.
46        required: usize,
47        /// Output units available from `output_index`.
48        available: usize,
49    },
50
51    /// The underlying codec or hook policy rejected finalization.
52    #[error("finish failed: {source}")]
53    Source {
54        /// Error returned by the concrete codec or hook policy.
55        #[source]
56        source: E,
57    },
58}
59
60impl<E> FinishError<E> {
61    /// Creates a finish error from capacity planning failure.
62    ///
63    /// # Parameters
64    ///
65    /// - `source`: Capacity planning error reported while computing the finish bound.
66    ///
67    /// # Returns
68    ///
69    /// Returns a finish error wrapping `source`.
70    #[must_use]
71    #[inline(always)]
72    pub const fn capacity(source: CapacityError) -> Self {
73        Self::Capacity { source }
74    }
75
76    /// Creates an invalid-output-index finish error.
77    ///
78    /// # Parameters
79    ///
80    /// - `index`: Output index supplied by the caller.
81    /// - `len`: Length of the output slice.
82    ///
83    /// # Returns
84    ///
85    /// Returns an invalid-output-index finish error.
86    #[must_use]
87    #[inline(always)]
88    pub const fn invalid_output_index(index: usize, len: usize) -> Self {
89        Self::InvalidOutputIndex { index, len }
90    }
91
92    /// Creates an insufficient-output finish error.
93    ///
94    /// # Parameters
95    ///
96    /// - `output_index`: Output index supplied by the caller.
97    /// - `required`: Output units required to finish in one call.
98    /// - `available`: Output units available from `output_index`.
99    ///
100    /// # Returns
101    ///
102    /// Returns an insufficient-output finish error.
103    #[must_use]
104    #[inline(always)]
105    pub const fn insufficient_output(output_index: usize, required: usize, available: usize) -> Self {
106        Self::InsufficientOutput {
107            output_index,
108            required,
109            available,
110        }
111    }
112
113    /// Creates a finish error from an underlying semantic error.
114    ///
115    /// # Parameters
116    ///
117    /// - `source`: Error returned by the concrete codec or hook policy.
118    ///
119    /// # Returns
120    ///
121    /// Returns a finish error wrapping `source`.
122    #[must_use]
123    #[inline(always)]
124    pub const fn source(source: E) -> Self {
125        Self::Source { source }
126    }
127
128    /// Maps the underlying semantic error while preserving capacity failures.
129    ///
130    /// # Type Parameters
131    ///
132    /// - `T`: Target semantic error type.
133    /// - `F`: Mapping function type.
134    ///
135    /// # Parameters
136    ///
137    /// - `map`: Function used to map the underlying semantic error.
138    ///
139    /// # Returns
140    ///
141    /// Returns a finish error with the mapped semantic error type.
142    #[inline]
143    pub fn map_source<T, F>(self, map: F) -> FinishError<T>
144    where
145        F: FnOnce(E) -> T,
146    {
147        match self {
148            Self::Capacity { source } => FinishError::Capacity { source },
149            Self::InvalidOutputIndex { index, len } => FinishError::InvalidOutputIndex { index, len },
150            Self::InsufficientOutput {
151                output_index,
152                required,
153                available,
154            } => FinishError::InsufficientOutput {
155                output_index,
156                required,
157                available,
158            },
159            Self::Source { source } => FinishError::Source { source: map(source) },
160        }
161    }
162
163    /// Validates that an output slice can hold one-shot final output.
164    ///
165    /// # Parameters
166    ///
167    /// - `output_len`: Length of the output slice.
168    /// - `output_index`: Output index supplied by the caller.
169    /// - `required`: Output units required to finish in one call.
170    ///
171    /// # Returns
172    ///
173    /// Returns `Ok(())` when output capacity is sufficient.
174    ///
175    /// # Errors
176    ///
177    /// Returns [`FinishError::InvalidOutputIndex`] when `output_index` is beyond
178    /// the slice, or [`FinishError::InsufficientOutput`] when fewer than
179    /// `required` units are writable from `output_index`.
180    #[inline]
181    pub fn ensure_output_capacity(output_len: usize, output_index: usize, required: usize) -> Result<(), Self> {
182        if output_index > output_len {
183            return Err(Self::invalid_output_index(output_index, output_len));
184        }
185        let available = output_len - output_index;
186        if available < required {
187            return Err(Self::insufficient_output(output_index, required, available));
188        }
189        Ok(())
190    }
191}