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