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 for a codec-backed decode hook.
15pub type DecodeInvalidActionOf<C> = DecodeInvalidAction<<C as Codec>::Value>;
16
17/// Action selected after a codec reports invalid encoded input.
18///
19/// Incomplete input is not a policy action. Codecs report it with
20/// [`crate::DecodeFailure::Incomplete`], and the decode engine converts it
21/// directly into [`crate::TranscodeStatus::NeedInput`].
22///
23/// # Type Parameters
24///
25/// - `Value`: Decoded output value type.
26///
27/// # Examples
28///
29/// ```
30/// use core::num::NonZeroUsize;
31/// use qubit_codec::engine::DecodeInvalidAction;
32///
33/// let action = DecodeInvalidAction::<u8>::Skip {
34///     consumed: NonZeroUsize::new(1).unwrap(),
35/// };
36/// assert!(matches!(action, DecodeInvalidAction::Skip { .. }));
37/// ```
38#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
39#[non_exhaustive]
40pub enum DecodeInvalidAction<Value> {
41    /// Reject the current invalid input.
42    ///
43    /// The decode engine reports the original codec decode error as
44    /// [`crate::TranscodeDecodeError::Domain`] at the current input index.
45    Reject,
46
47    /// Consume invalid input without producing output.
48    ///
49    /// `consumed` must not exceed the hook context's available input count.
50    /// Over-consuming is a hook contract violation and panics in the engine.
51    Skip {
52        /// Source units to consume.
53        consumed: NonZeroUsize,
54    },
55
56    /// Produce one replacement value and consume source units.
57    ///
58    /// `consumed` must not exceed the hook context's available input count.
59    /// Over-consuming is a hook contract violation and panics in the engine.
60    Emit {
61        /// Value to write to the output buffer.
62        value: Value,
63        /// Source units to consume.
64        consumed: NonZeroUsize,
65    },
66}
67
68impl<Value> DecodeInvalidAction<Value> {
69    /// Validates a policy-reported consumed source-unit count against available
70    /// input.
71    ///
72    /// # Parameters
73    ///
74    /// - `consumed`: Source units requested by the concrete policy.
75    /// - `available`: Source units visible from the current decode cursor.
76    ///
77    /// # Returns
78    ///
79    /// Returns the validated non-zero consumed count.
80    ///
81    /// # Panics
82    ///
83    /// Panics when `available == 0` or when `consumed > available`.
84    #[inline(always)]
85    #[must_use]
86    pub(super) fn bound_consumed(consumed: NonZeroUsize, available: usize) -> NonZeroUsize {
87        assert!(available > 0, "DecodeInvalidAction cannot consume empty input",);
88        assert!(
89            consumed.get() <= available,
90            "DecodeInvalidAction consumed units must not exceed available input",
91        );
92        consumed
93    }
94}