Skip to main content

qubit_codec/transcode/engine/
decode_invalid_action.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//! Invalid-decode actions returned by buffered decoder policy hooks.
9
10use core::num::NonZeroUsize;
11
12use crate::Codec;
13
14/// Invalid-decode action type used by a codec-backed decode hook.
15///
16/// This alias binds the action's replacement value to the codec's decoded
17/// value type, so hook implementations can name their result in terms of the
18/// codec they serve.
19///
20/// # Type Parameters
21///
22/// - `C`: Codec whose decoded value is carried by replacement actions.
23pub type DecodeInvalidActionOf<C> = DecodeInvalidAction<<C as Codec>::Value>;
24
25/// Action selected after a codec reports invalid encoded input.
26///
27/// While the stream is open, incomplete input is not an invalid-input policy
28/// action. Codecs report it with [`crate::DecodeFailure::Incomplete`], and the
29/// decode engine converts it into [`crate::TranscodeStatus::NeedInput`]. After
30/// upstream EOF, [`crate::Transcoder::transcode_eof`] asks the codec to resolve
31/// a trailing incomplete sequence through [`crate::Codec::decode_eof`]; if it
32/// remains incomplete, the engine calls
33/// [`crate::engine::DecodeIncompleteAction`] through
34/// [`crate::engine::TranscodeDecodeHooks::handle_incomplete_decode`].
35///
36/// # Type Parameters
37///
38/// - `Value`: Decoded output value type.
39///
40/// # Examples
41///
42/// ```
43/// use core::num::NonZeroUsize;
44///
45/// use qubit_codec::engine::DecodeInvalidAction;
46///
47/// let action = DecodeInvalidAction::<u8>::Skip {
48///     consumed: NonZeroUsize::new(1).unwrap(),
49/// };
50/// assert!(matches!(action, DecodeInvalidAction::Skip { .. }));
51/// ```
52#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
53#[non_exhaustive]
54#[must_use]
55pub enum DecodeInvalidAction<Value> {
56    /// Reject the current invalid input.
57    ///
58    /// The decode engine reports the original codec decode error as
59    /// [`crate::TranscodeDecodeError::Domain`] at the current input index.
60    Reject,
61
62    /// Consume invalid input without producing output.
63    ///
64    /// `consumed` must not exceed the hook context's available input count.
65    /// Over-consuming is a hook contract violation and panics in the engine.
66    Skip {
67        /// Source units to consume.
68        consumed: NonZeroUsize,
69    },
70
71    /// Produce one replacement value and consume source units.
72    ///
73    /// `consumed` must not exceed the hook context's available input count.
74    /// Over-consuming is a hook contract violation and panics in the engine.
75    Emit {
76        /// Value to write to the output buffer.
77        value: Value,
78        /// Source units to consume.
79        consumed: NonZeroUsize,
80    },
81}
82
83impl<Value> DecodeInvalidAction<Value> {
84    /// Validates a policy-reported consumed source-unit count against available
85    /// input.
86    ///
87    /// # Parameters
88    ///
89    /// - `consumed`: Source units requested by the concrete policy.
90    /// - `available`: Source units visible from the current decode cursor.
91    ///
92    /// # Returns
93    ///
94    /// Returns the validated non-zero consumed count.
95    ///
96    /// # Panics
97    ///
98    /// Panics when `available == 0` or when `consumed > available`.
99    #[inline]
100    #[must_use]
101    pub(super) fn bound_consumed(consumed: NonZeroUsize, available: usize) -> NonZeroUsize {
102        assert!(available > 0, "DecodeInvalidAction cannot consume empty input",);
103        assert!(
104            consumed.get() <= available,
105            "DecodeInvalidAction consumed units must not exceed available input",
106        );
107        consumed
108    }
109}