Skip to main content

qubit_codec/transcode/
transcode_decode_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 decoders.
9
10use core::num::NonZeroUsize;
11
12use thiserror::Error;
13
14use super::capacity_error::CapacityError;
15use super::transcode_domain_error::TranscodeDomainError;
16use super::transcode_failure::TranscodeFailure;
17use crate::Codec;
18use crate::DecodeFailure;
19
20/// Decode transcode error for a codec-backed decoder.
21pub type TranscodeDecodeErrorOf<C> = TranscodeDecodeError<<C as Codec>::DecodeError>;
22
23/// Error reported by a decode-oriented transcode operation.
24#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
25pub enum TranscodeDecodeError<E> {
26    /// Framework-level transcode failure.
27    #[error(transparent)]
28    Failure(#[from] TranscodeFailure),
29
30    /// Domain-specific codec, charset, or policy error.
31    #[error(transparent)]
32    Domain(#[from] TranscodeDomainError<E>),
33}
34
35impl<E> TranscodeDecodeError<E> {
36    /// Creates a reset-phase domain-specific transcode error.
37    #[inline(always)]
38    pub const fn domain_reset(source: E) -> Self {
39        Self::Domain(TranscodeDomainError::reset(source))
40    }
41
42    /// Creates a main-phase domain-specific transcode error.
43    #[inline(always)]
44    pub const fn domain_main(source: E, input_index: usize) -> Self {
45        Self::Domain(TranscodeDomainError::main(source, input_index))
46    }
47
48    /// Creates a main-phase domain-specific transcode error with decode
49    /// consumption context.
50    #[inline(always)]
51    pub const fn domain_main_with_consumed(
52        source: E,
53        input_index: usize,
54        input_consumed: Option<NonZeroUsize>,
55    ) -> Self {
56        Self::Domain(TranscodeDomainError::main_with_consumed(
57            source,
58            input_index,
59            input_consumed,
60        ))
61    }
62
63    /// Creates a finish-phase domain-specific transcode error.
64    #[inline(always)]
65    pub const fn domain_finish(source: E) -> Self {
66        Self::Domain(TranscodeDomainError::finish(source))
67    }
68
69    /// Converts a low-level decode failure into a decode transcode error.
70    #[inline]
71    #[must_use]
72    pub fn from_decode_failure(failure: DecodeFailure<E>, input_index: usize, available: usize) -> Self {
73        match failure {
74            DecodeFailure::Incomplete {
75                source: Some(source), ..
76            } => Self::domain_main(source, input_index),
77            DecodeFailure::Incomplete { required_total, .. } => {
78                TranscodeFailure::incomplete_input(input_index, required_total.get(), available).into()
79            }
80            DecodeFailure::Invalid { source, consumed } => {
81                Self::domain_main_with_consumed(source, input_index, consumed)
82            }
83        }
84    }
85
86    /// Returns whether this error wraps a domain error.
87    #[inline(always)]
88    #[must_use]
89    pub const fn is_domain(&self) -> bool {
90        matches!(self, Self::Domain(_))
91    }
92
93    /// Returns the framework failure carried by this error.
94    #[inline(always)]
95    #[must_use]
96    pub const fn failure_ref(&self) -> Option<&TranscodeFailure> {
97        match self {
98            Self::Failure(failure) => Some(failure),
99            Self::Domain(_) => None,
100        }
101    }
102
103    /// Borrows the wrapped domain error and transcode context.
104    #[inline(always)]
105    #[must_use]
106    pub const fn domain_error_ref(&self) -> Option<&TranscodeDomainError<E>> {
107        match self {
108            Self::Domain(error) => Some(error),
109            Self::Failure(_) => None,
110        }
111    }
112
113    /// Borrows the wrapped domain error.
114    #[must_use]
115    pub const fn domain_ref(&self) -> Option<&E> {
116        match self {
117            Self::Domain(error) => Some(error.source()),
118            Self::Failure(_) => None,
119        }
120    }
121
122    /// Maps the wrapped domain error while preserving framework failures.
123    #[inline]
124    pub fn map_domain<F, T>(self, f: F) -> TranscodeDecodeError<T>
125    where
126        F: FnOnce(E) -> T,
127    {
128        match self {
129            Self::Failure(failure) => TranscodeDecodeError::Failure(failure),
130            Self::Domain(error) => TranscodeDecodeError::Domain(error.map_source(f)),
131        }
132    }
133}
134
135impl<E> From<CapacityError> for TranscodeDecodeError<E> {
136    /// Converts capacity planning errors into transcode framework errors.
137    #[inline(always)]
138    fn from(error: CapacityError) -> Self {
139        TranscodeFailure::from(error).into()
140    }
141}