wire_repr/codec/range_source.rs
1//! Range-source conversion contract.
2
3use super::FixedCodec;
4
5/// Performs checked bidirectional structural conversion between a decoded fixed source
6/// representation and byte geometry.
7///
8/// A range uses the geometry as either a relative byte length (`bytes(source)`) or an
9/// exclusive endpoint relative to representation byte zero (`bytes_to(source)`). Generated
10/// parsers call [`Self::to_bytes`] at the consuming range before their existing checked
11/// range and input-bounds logic. During [`crate::PreparedLayout`] preparation, generated
12/// builders derive required geometry, require shared sources to agree on that geometry, and
13/// call [`Self::from_bytes`] once for each source before planning its physical codec. Commit
14/// only writes the prepared plan and remains capacity-only and atomic.
15///
16/// Supported source values and byte geometries must round-trip coherently. Implementations
17/// must use checked arithmetic and return an explicit error for structural underflow,
18/// alignment, or encoded-field bounds they cannot convert. Unrelated protocol policy
19/// remains consumer-owned.
20///
21/// Macro adapters are supported only on direct built-in integer fixed fields that physically
22/// precede at least one range. This is a current hard ownership boundary: custom
23/// [`FixedCodec`] values or plans can require self-referential prepared storage. The
24/// adapter does not create a geometry getter; the ordinary source getter remains the raw
25/// wire integer. Unsigned bit projections may coexist with an adapter and read that whole
26/// integer; the adapter also consumes and returns the whole packed value.
27pub trait RangeSource<C: FixedCodec> {
28 /// Error returned when converting between the fixed representation and byte geometry.
29 type Error: core::fmt::Debug;
30
31 /// Converts a decoded fixed source value to a byte length or absolute endpoint.
32 fn to_bytes(value: C::Value<'_>) -> Result<usize, Self::Error>;
33
34 /// Converts a byte length or absolute endpoint to an owned fixed source value.
35 fn from_bytes(bytes: usize) -> Result<C::Value<'static>, Self::Error>;
36}
37
38#[cfg(test)]
39mod tests {
40 use super::RangeSource;
41 use crate::{BeU16, FixedCodec, U8};
42
43 struct U8Length;
44
45 impl RangeSource<U8> for U8Length {
46 type Error = ();
47
48 fn to_bytes(value: <U8 as FixedCodec>::Value<'_>) -> Result<usize, Self::Error> {
49 Ok(usize::from(value))
50 }
51
52 fn from_bytes(bytes: usize) -> Result<<U8 as FixedCodec>::Value<'static>, Self::Error> {
53 u8::try_from(bytes).map_err(|_| ())
54 }
55 }
56
57 struct BeU16Length;
58
59 impl RangeSource<BeU16> for BeU16Length {
60 type Error = ();
61
62 fn to_bytes(value: <BeU16 as FixedCodec>::Value<'_>) -> Result<usize, Self::Error> {
63 Ok(usize::from(value))
64 }
65
66 fn from_bytes(bytes: usize) -> Result<<BeU16 as FixedCodec>::Value<'static>, Self::Error> {
67 u16::try_from(bytes).map_err(|_| ())
68 }
69 }
70
71 #[test]
72 fn local_integer_adapters_need_no_allocation() {
73 assert_eq!(U8Length::to_bytes(7), Ok(7));
74 assert_eq!(U8Length::from_bytes(255), Ok(255));
75 assert_eq!(BeU16Length::to_bytes(0x1234), Ok(0x1234));
76 assert_eq!(BeU16Length::from_bytes(0xffff), Ok(0xffff));
77 }
78}