Skip to main content

qubit_codec/transcode/
transcode_encode_error.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Errors reported by transcode encoders.
9
10use thiserror::Error;
11
12use super::{
13    capacity_error::CapacityError,
14    transcode_domain_error::TranscodeDomainError,
15    transcode_failure::TranscodeFailure,
16};
17use crate::Codec;
18
19/// Encode transcode error for a codec-backed encoder.
20pub type TranscodeEncodeErrorOf<C> =
21    TranscodeEncodeError<<C as Codec>::EncodeError, <C as Codec>::Value>;
22
23/// Error reported by an encode-oriented transcode operation.
24#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
25pub enum TranscodeEncodeError<E, V> {
26    /// Framework-level transcode failure.
27    #[error(transparent)]
28    Failure(#[from] TranscodeFailure),
29
30    /// The input value cannot be encoded by the target codec and policy.
31    #[error("unencodable value at input index {input_index}")]
32    Unencodable {
33        /// Absolute input index of the value being encoded.
34        input_index: usize,
35        /// Value being encoded, when the transcoder can expose it.
36        value: Option<V>,
37    },
38
39    /// Domain-specific codec, charset, or policy error.
40    #[error(transparent)]
41    Domain(#[from] TranscodeDomainError<E>),
42}
43
44impl<E, V> TranscodeEncodeError<E, V> {
45    /// Creates a reset-phase domain-specific transcode error.
46    #[inline(always)]
47    pub const fn domain_reset(source: E) -> Self {
48        Self::Domain(TranscodeDomainError::reset(source))
49    }
50
51    /// Creates a main-phase domain-specific transcode error.
52    #[inline(always)]
53    pub const fn domain_main(source: E, input_index: usize) -> Self {
54        Self::Domain(TranscodeDomainError::main(source, input_index))
55    }
56
57    /// Creates a finish-phase domain-specific transcode error.
58    #[inline(always)]
59    pub const fn domain_finish(source: E) -> Self {
60        Self::Domain(TranscodeDomainError::finish(source))
61    }
62
63    /// Creates an unencodable-value error with value context.
64    #[inline(always)]
65    #[must_use]
66    pub fn unencodable(input_index: usize, value: V) -> Self {
67        Self::Unencodable {
68            input_index,
69            value: Some(value),
70        }
71    }
72
73    /// Creates an unencodable-value error without value context.
74    #[inline(always)]
75    #[must_use]
76    pub const fn unencodable_without_context(input_index: usize) -> Self {
77        Self::Unencodable {
78            input_index,
79            value: None,
80        }
81    }
82
83    /// Returns whether this error wraps a domain error.
84    #[inline(always)]
85    #[must_use]
86    pub const fn is_domain(&self) -> bool {
87        matches!(self, Self::Domain(_))
88    }
89
90    /// Returns the framework failure carried by this error.
91    #[inline(always)]
92    #[must_use]
93    pub const fn failure_ref(&self) -> Option<&TranscodeFailure> {
94        match self {
95            Self::Failure(failure) => Some(failure),
96            Self::Unencodable { .. } | Self::Domain(_) => None,
97        }
98    }
99
100    /// Borrows the wrapped domain error and transcode context.
101    #[inline(always)]
102    #[must_use]
103    pub const fn domain_error_ref(&self) -> Option<&TranscodeDomainError<E>> {
104        match self {
105            Self::Domain(error) => Some(error),
106            Self::Failure(_) | Self::Unencodable { .. } => None,
107        }
108    }
109
110    /// Borrows the wrapped domain error.
111    #[must_use]
112    pub const fn domain_ref(&self) -> Option<&E> {
113        match self {
114            Self::Domain(error) => Some(error.source()),
115            Self::Failure(_) | Self::Unencodable { .. } => None,
116        }
117    }
118
119    /// Borrows the unencodable value context carried by this error.
120    #[inline(always)]
121    #[must_use]
122    pub const fn unencodable_ref(&self) -> Option<(usize, Option<&V>)> {
123        match self {
124            Self::Unencodable { input_index, value } => {
125                Some((*input_index, value.as_ref()))
126            }
127            Self::Failure(_) | Self::Domain(_) => None,
128        }
129    }
130
131    /// Maps the wrapped domain error while preserving other errors.
132    #[inline]
133    pub fn map_domain<F, T>(self, f: F) -> TranscodeEncodeError<T, V>
134    where
135        F: FnOnce(E) -> T,
136    {
137        match self {
138            Self::Failure(failure) => TranscodeEncodeError::Failure(failure),
139            Self::Unencodable { input_index, value } => {
140                TranscodeEncodeError::Unencodable { input_index, value }
141            }
142            Self::Domain(error) => {
143                TranscodeEncodeError::Domain(error.map_source(f))
144            }
145        }
146    }
147
148    /// Maps value context carried by unencodable-value errors.
149    #[inline]
150    pub fn map_value<F, W>(self, f: F) -> TranscodeEncodeError<E, W>
151    where
152        F: FnOnce(V) -> W,
153    {
154        match self {
155            Self::Failure(failure) => TranscodeEncodeError::Failure(failure),
156            Self::Unencodable { input_index, value } => {
157                TranscodeEncodeError::Unencodable {
158                    input_index,
159                    value: value.map(f),
160                }
161            }
162            Self::Domain(error) => TranscodeEncodeError::Domain(error),
163        }
164    }
165}
166
167impl<E, V> From<CapacityError> for TranscodeEncodeError<E, V> {
168    /// Converts capacity planning errors into transcode framework errors.
169    #[inline(always)]
170    fn from(error: CapacityError) -> Self {
171        TranscodeFailure::from(error).into()
172    }
173}