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