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}