1use super::EncodePlan;
4
5pub trait FixedCodec {
22 type Value<'wire>
24 where
25 Self: 'wire;
26
27 type EncodeError: core::fmt::Debug;
29
30 type Plan<'value>: EncodePlan
32 where
33 Self: 'value;
34
35 const WIDTH: usize;
37
38 fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire>;
40
41 fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError>;
43}
44
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub struct ExactWidthError {
48 expected: usize,
49 actual: usize,
50}
51
52impl ExactWidthError {
53 #[must_use]
55 pub const fn new(expected: usize, actual: usize) -> Self {
56 Self { expected, actual }
57 }
58
59 #[must_use]
61 pub const fn expected(&self) -> usize {
62 self.expected
63 }
64
65 #[must_use]
67 pub const fn actual(&self) -> usize {
68 self.actual
69 }
70}
71
72impl core::fmt::Display for ExactWidthError {
73 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 write!(
75 formatter,
76 "fixed codec expected {} bytes, got {}",
77 self.expected, self.actual
78 )
79 }
80}
81
82impl core::error::Error for ExactWidthError {}
83
84#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
99pub struct Bytes<const N: usize>;
100
101impl<const N: usize> FixedCodec for Bytes<N> {
102 type Value<'wire>
103 = &'wire [u8]
104 where
105 Self: 'wire;
106 type EncodeError = ExactWidthError;
107 type Plan<'value>
108 = &'value [u8]
109 where
110 Self: 'value;
111
112 const WIDTH: usize = {
113 assert!(N != 0, "Bytes<N> requires a nonzero width");
114 N
115 };
116
117 #[inline]
118 fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire> {
119 const { assert!(N != 0, "Bytes<N> requires a nonzero width") };
120 bytes
121 }
122
123 #[inline]
124 fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError> {
125 const { assert!(N != 0, "Bytes<N> requires a nonzero width") };
126 if value.len() == N {
127 Ok(value)
128 } else {
129 Err(ExactWidthError::new(N, value.len()))
130 }
131 }
132}
133
134#[doc(hidden)]
145#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
146pub struct OwnedBytes<const N: usize>;
147
148impl<const N: usize> FixedCodec for OwnedBytes<N> {
149 type Value<'wire>
150 = [u8; N]
151 where
152 Self: 'wire;
153 type EncodeError = core::convert::Infallible;
154 type Plan<'value>
155 = [u8; N]
156 where
157 Self: 'value;
158
159 const WIDTH: usize = {
160 assert!(N != 0, "OwnedBytes<N> requires a nonzero width");
161 N
162 };
163
164 #[inline]
165 fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire> {
166 const { assert!(N != 0, "OwnedBytes<N> requires a nonzero width") };
167 let mut value = [0_u8; N];
168 value.copy_from_slice(bytes);
169 value
170 }
171
172 #[inline]
173 fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError> {
174 const { assert!(N != 0, "OwnedBytes<N> requires a nonzero width") };
175 Ok(value)
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::{FixedCodec, OwnedBytes};
182 use crate::codec::EncodePlan;
183
184 #[test]
185 fn owned_bytes_decode_copies_exact_input() {
186 let mut source = [1, 2, 3, 4];
187 let decoded = <OwnedBytes<4> as FixedCodec>::decode(&source);
188
189 source[0] = 9;
190 assert_eq!(decoded, [1, 2, 3, 4]);
191 }
192
193 #[test]
194 fn owned_bytes_plan_is_infallible_and_writes_exact_bytes() {
195 let plan = <OwnedBytes<4> as FixedCodec>::plan([1, 2, 3, 4]).unwrap();
196 let mut output = [0; 4];
197
198 assert_eq!(plan.encoded_len(), 4);
199 plan.write_into(&mut output);
200 assert_eq!(output, [1, 2, 3, 4]);
201 }
202
203 #[test]
204 fn owned_bytes_width_is_nonzero() {
205 assert_eq!(<OwnedBytes<1> as FixedCodec>::WIDTH, 1);
206 }
207}