Skip to main content

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.