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}