slice_codec/encode_into.rs
1// Copyright (c) ZeroC, Inc.
2
3use crate::buffer::OutputTarget;
4use crate::encoder::Encoder;
5use crate::Result;
6
7/// TODO
8pub trait EncodeInto: Sized {
9 /// Encodes a value of this type with the provided encoder.
10 fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()>;
11}
12
13// =============================================================================
14// Macros to streamline implementing `EncodeInto` on common types.
15// =============================================================================
16
17/// This macro implements [`EncodeInto`] on a borrowed type by copying it, then calling `encode_into` on the copied
18/// value. For this implementation on `&T` to work, `T` must be [`Copy`] and implement [`EncodeInto`] itself.
19#[doc(hidden)]
20#[macro_export]
21macro_rules! implement_encode_into_on_borrowed_type {
22 ($ty:ty) => {
23 impl EncodeInto for &$ty {
24 // TODO: this generated broken links. [https://github.com/rust-lang/rust/issues/54172]
25 #[doc = concat!("Delegates to [", stringify!($ty), "::encode_into].")]
26 fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
27 (*self).encode_into(encoder)
28 }
29 }
30 };
31}
32pub use implement_encode_into_on_borrowed_type; // Re-export the macro so it can be used in other modules.
33
34/// This macro implements [`EncodeInto`] on a numeric primitive type by calling `to_le_bytes` on it, which returns its
35/// in-memory representation in little-endian format. We have to use a macro because there is no common trait for
36/// numeric types, they all just happen to have this function with the same name and behavior.
37///
38/// This works out-of-the-box because both Rust and Slice use two's complement for representing signed integers,
39/// and IEEE-754 for floating point numbers.
40#[doc(hidden)]
41#[macro_export]
42macro_rules! implement_encode_into_on_numeric_primitive_type {
43 ($ty:ty, $doc_text:literal) => {
44 impl EncodeInto for $ty {
45 #[doc = $doc_text]
46 fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
47 let bytes = self.to_le_bytes();
48 encoder.write_bytes_exact(&bytes)
49 }
50 }
51 implement_encode_into_on_borrowed_type!($ty);
52 };
53}
54pub use implement_encode_into_on_numeric_primitive_type; // Re-export the macro so it can be used in other modules.
55
56/// This macro implements [`EncodeInto`] on a dictionary type by first encoding its length (the number of entries),
57/// followed by encoding each key-value pair individually. We have to use a macro because there is no common trait for
58/// dictionary types. If the dictionary entries are ordered, their order is preserved during the encoding process.
59#[doc(hidden)]
60#[macro_export]
61macro_rules! impl_encode_into_on_dictionary_type {
62 ($ty:ident, $doc_text:literal) => {
63 impl<'a, K, V> EncodeInto for &'a $ty<K, V>
64 where
65 &'a K: EncodeInto,
66 &'a V: EncodeInto,
67 {
68 #[doc = $doc_text]
69 fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
70 encoder.encode_size(self.len())?;
71 for (key, value) in self {
72 encoder.encode(key)?;
73 encoder.encode(value)?;
74 }
75 Ok(())
76 }
77 }
78 };
79}
80pub use impl_encode_into_on_dictionary_type; // Re-export the macro so it can be used in other modules.