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