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}