xdrs/encodes_as.rs
1//! Declaring that one type serializes like another.
2//!
3//! [`EncodesAs<X>`] is the Rust counterpart of x23's `serialize_equiv`: it
4//! lets an API whose wire type is `X` accept any value that encodes to the
5//! same bytes, so a caller can serialize from borrowed or shared storage
6//! instead of first building an owned `X`.
7
8use crate::write_xdr::WriteXDR;
9use xdr_common::{BytesM, StaticIfNoAlloc, StringM, VecM};
10
11/// Types whose XDR encoding can stand in for an `X`.
12///
13/// `T: EncodesAs<X>` promises that [`WriteXDR::write_xdr`] on a `T` writes
14/// exactly the bytes that `X` would write for the corresponding value.
15/// There is no separate write method: code bounded on `EncodesAs<X>` encodes
16/// through the `WriteXDR` supertrait. Nothing checks the promise at runtime,
17/// and in particular `[U]` standing in for a `VecM<X, N>` does not check
18/// that its length is at most `N`; an oversized value encodes, and the
19/// decoder on the other end rejects it.
20///
21/// The implementations are:
22///
23/// - **Identity**: every type this crate implements `WriteXDR` for, except
24/// `&T`, `&mut T`, `Box<T>` and `Arc<T>`, is `EncodesAs<Self>`, as is every type with
25/// `#[derive(WriteXDR)]`. A hand-written `WriteXDR` impl should add
26/// `impl EncodesAs<Self> for T {}` alongside it.
27/// - **Pointers**: `&U`, `Box<U>` and `Arc<U>` are `EncodesAs<X>` whenever
28/// `U: EncodesAs<X>`.
29/// - **Slices**: `[U]` is `EncodesAs<VecM<X, N>>` whenever `U: EncodesAs<X>`.
30/// - **Chunks**: [`ChunkedBytes<_, N>`](crate::ChunkedBytes) is
31/// `EncodesAs<BytesM<N>>`, and [`ChunkedVec<_, N>`](crate::ChunkedVec) is
32/// `EncodesAs<VecM<X, N>>` whenever its elements are `EncodesAs<X>`.
33///
34/// There is no blanket identity `impl<X: WriteXDR> EncodesAs<X> for X`:
35/// because `&U` and `Box<U>` are themselves `WriteXDR`, it would overlap the
36/// pointer impls, which coherence rejects.
37///
38/// # Example
39///
40/// An RPC client that takes its argument as `&(impl EncodesAs<X> + ?Sized)`
41/// accepts both the owned wire type and a slice of shared elements:
42///
43/// ```rust
44/// # #[cfg(feature = "alloc")] {
45/// use std::sync::Arc;
46/// use xdrs::{EncodesAs, VecM, txs};
47///
48/// fn send(batch: &(impl EncodesAs<VecM<u32, 8>> + ?Sized)) -> Vec<u8> {
49/// txs::encode(batch).unwrap()
50/// }
51///
52/// let owned: VecM<u32, 8> = vec![1, 2].try_into().unwrap();
53/// let shared: Vec<Arc<u32>> = vec![Arc::new(1), Arc::new(2)];
54/// assert_eq!(send(&owned), send(&shared[..]));
55/// # }
56/// ```
57pub trait EncodesAs<X: ?Sized>: WriteXDR {}
58
59impl<X: ?Sized, U: EncodesAs<X> + ?Sized> EncodesAs<X> for &U {}
60
61#[cfg(feature = "alloc")]
62#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
63impl<X: ?Sized, U: EncodesAs<X> + ?Sized> EncodesAs<X> for alloc::boxed::Box<U> {}
64
65#[cfg(not(feature = "alloc"))]
66impl<X: ?Sized, U: EncodesAs<X>> EncodesAs<X> for xdr_common::Box<U> {}
67
68#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
69#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
70impl<X: ?Sized, U: EncodesAs<X> + ?Sized> EncodesAs<X> for alloc::sync::Arc<U> {}
71
72impl<X: StaticIfNoAlloc, U: EncodesAs<X>, const N: u32> EncodesAs<VecM<X, N>> for [U] {}
73
74// Identity impls. These must track the `WriteXDR` impls in `write_xdr`,
75// `int128` and `heapless`, leaving out the pointer types forwarded above.
76
77macro_rules! encodes_as_self {
78 ( $( $t:ty ),* $(,)? ) => { $(
79 impl EncodesAs<$t> for $t {}
80 )* };
81}
82
83encodes_as_self! {
84 i16, i32, i64, u16, u32, u64, bool, f32, f64, str, (), core::convert::Infallible,
85}
86
87#[cfg(feature = "int128")]
88encodes_as_self! { i128, u128 }
89
90#[cfg(feature = "alloc")]
91encodes_as_self! { alloc::string::String }
92
93// Generic types whose `WriteXDR` impls have bounds; `where Self: WriteXDR`
94// keeps those bounds in one place, and also lets one impl cover the opaque
95// (`u8`) and element-wise variants of `[T]`, `[T; N]` and the vector types.
96macro_rules! encodes_as_self_where_write_xdr {
97 ( $( [$($g:tt)*] $t:ty ),* $(,)? ) => { $(
98 impl<$($g)*> EncodesAs<$t> for $t where $t: WriteXDR {}
99 )* };
100}
101
102encodes_as_self_where_write_xdr! {
103 [T] [T],
104 [T, const N: usize] [T; N],
105 [T] Option<T>,
106 [T, E] Result<T, E>,
107 [T] core::marker::PhantomData<T>,
108 [const MAX: u32] BytesM<MAX>,
109 [const MAX: u32] StringM<MAX>,
110 [T: StaticIfNoAlloc, const MAX: u32] VecM<T, MAX>,
111}
112
113#[cfg(feature = "alloc")]
114encodes_as_self_where_write_xdr! { [T] alloc::vec::Vec<T> }
115
116#[cfg(feature = "heapless")]
117encodes_as_self_where_write_xdr! {
118 [T, const N: usize] ::heapless::Vec<T, N>,
119 [const N: usize] ::heapless::String<N>,
120}
121
122macro_rules! encodes_as_self_tuples {
123 () => {};
124 ( $t0:ident $(, $t:ident)* ) => {
125 encodes_as_self_where_write_xdr! { [$t0 $(, $t)*] ($t0, $($t,)*) }
126 encodes_as_self_tuples! { $($t),* }
127 };
128}
129
130encodes_as_self_tuples! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 }