commonware_codec/extensions.rs
1//! Extension traits for ergonomic operations on encoding and decoding.
2//!
3//! These traits provide convenience methods (like `read()`, `decode()`, `read_range()`, and
4//! `decode_range()`) that simplify common use cases of the core [Read] and [Decode] traits,
5//! particularly when default configurations (`()`) or [RangeCfg] are involved.
6
7use crate::{Decode, Error, RangeCfg, Read};
8use bytes::Buf;
9use core::ops::RangeBounds;
10
11/// Extension trait providing ergonomic read method for types requiring no configuration
12/// (i.e. `Cfg = ()`).
13///
14/// Import this trait to use the `.read(buf)` method as a shorthand for `.read_cfg(buf, ())`.
15pub trait ReadExt: Read<Cfg = ()> {
16    /// Reads a value using the default `()` config.
17    fn read(buf: &mut impl Buf) -> Result<Self, Error> {
18        Self::read_cfg(buf, &())
19    }
20}
21
22// Automatically implement `ReadExt` for types that implement `Read` with no config.
23impl<T: Read<Cfg = ()>> ReadExt for T {}
24
25/// Trait for types that are unit-like, i.e. have only one possible value.
26///
27/// This is typically used to implement the `DefaultExt` trait for types that are unit-like.
28pub trait IsUnit: Default {}
29
30// Generate `IsUnit` implementations for types with only one possible value.
31impl IsUnit for () {}
32impl<T: IsUnit, const N: usize> IsUnit for [T; N] where [T; N]: Default {}
33
34// Generate `IsUnit` implementations for `IsUnit` tuples up to 12 elements.
35macro_rules! impl_is_unit_for_tuple {
36    ( $($T:ident),+ ) => {
37        impl<$($T),+> IsUnit for ( $($T),+ ) where $($T: IsUnit),+ {}
38    }
39}
40impl_is_unit_for_tuple!(A, B);
41impl_is_unit_for_tuple!(A, B, C);
42impl_is_unit_for_tuple!(A, B, C, D);
43impl_is_unit_for_tuple!(A, B, C, D, E);
44impl_is_unit_for_tuple!(A, B, C, D, E, F);
45impl_is_unit_for_tuple!(A, B, C, D, E, F, G);
46impl_is_unit_for_tuple!(A, B, C, D, E, F, G, H);
47impl_is_unit_for_tuple!(A, B, C, D, E, F, G, H, I);
48impl_is_unit_for_tuple!(A, B, C, D, E, F, G, H, I, J);
49impl_is_unit_for_tuple!(A, B, C, D, E, F, G, H, I, J, K);
50impl_is_unit_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
51
52/// Extension trait providing ergonomic decode method for types requiring no specific configuration.
53/// This is typically types with only one possible value, such as the unit type `()` or tuples of
54/// such types.
55///
56/// Import this trait to use the `.decode(buf)` method as a shorthand for
57/// `.decode_cfg(buf, &X::default())`.
58pub trait DecodeExt<X: IsUnit>: Decode<Cfg = X> {
59    /// Decodes a value using the default `()` config.
60    fn decode(buf: impl Buf) -> Result<Self, Error> {
61        Self::decode_cfg(buf, &X::default())
62    }
63}
64
65// Automatically implement `DecodeExt` for types that implement `Decode` with no config.
66impl<X: IsUnit, T: Decode<Cfg = X>> DecodeExt<X> for T {}
67
68/// Extension trait for reading types whose config is `(RangeCfg<usize>, X)` where `X` is [Default].
69///
70/// Useful for reading collections like [`Vec<T>`] where `T` implements [Read] with no specific
71/// configuration. Import this trait to use the `.read_range()` method.
72pub trait ReadRangeExt<X: IsUnit>: Read<Cfg = (RangeCfg<usize>, X)> {
73    /// Reads a value using only a range configuration.
74    ///
75    /// The inner configuration type `X` must be [IsUnit] and `X::default()` is used for it.
76    fn read_range(buf: &mut impl Buf, range: impl RangeBounds<usize>) -> Result<Self, Error> {
77        Self::read_cfg(buf, &(RangeCfg::new(range), X::default()))
78    }
79}
80
81// Automatically implement `ReadRangeExt` for types that implement `Read` with config
82// `(RangeCfg<usize>, X)`, where `X` is `IsUnit`.
83impl<X: IsUnit, U: Read<Cfg = (RangeCfg<usize>, X)>> ReadRangeExt<X> for U {}
84
85/// Extension trait for reading types whose config is `(RangeCfg<usize>, X)` where `X` is [IsUnit],
86/// ensuring the buffer is consumed.
87///
88/// Useful for decoding collections like [`Vec<T>`] where `T` implements [Read] with no specific
89/// configuration. Import this trait to use the `.decode_range()` method.
90pub trait DecodeRangeExt<X: IsUnit>: Decode<Cfg = (RangeCfg<usize>, X)> {
91    /// Decodes a value using only a range configuration.
92    ///
93    /// The inner configuration type `X` must be [IsUnit] and `X::default()` is used for it.
94    fn decode_range(buf: impl Buf, range: impl RangeBounds<usize>) -> Result<Self, Error> {
95        Self::decode_cfg(buf, &(RangeCfg::new(range), X::default()))
96    }
97}
98
99// Automatically implement `DecodeRangeExt` for types that implement `Decode` with config
100// `(RangeCfg<usize>, X)`, where `X` is `IsUnit`.
101impl<X: IsUnit, U: Decode<Cfg = (RangeCfg<usize>, X)>> DecodeRangeExt<X> for U {}