Skip to main content

xuko_net/
transport.rs

1//! [`TransportData`] structure
2
3use xuko_core::bytes::Bytes;
4
5/// The default compression level
6#[cfg(feature = "compress")]
7pub const DEFAULT_COMPRESSION_LEVEL: i32 = zstd::DEFAULT_COMPRESSION_LEVEL;
8
9/// Error that occurs when using [`TransportData`]
10#[derive(Debug, thiserror::Error)]
11pub enum TransportDataError {
12    /// I/O Error
13    #[error("IO Error: {0}")]
14    IOError(#[from] std::io::Error),
15}
16
17/// [`TransportData`] structure
18///
19/// The [`TransportData`] structure is intended to transfer a series of bytes across the network.
20///
21/// The bytes can be optionally compressed if the `compress` feature is
22/// enabled in which case the [`TransportData::unwrap`] function will try to
23/// decompress the data.
24///
25/// The compression algorithm used is zstd.
26///
27/// # Examples
28///
29/// ```
30/// use xuko_net::transport::TransportData;
31///
32/// TransportData::new("hello".as_bytes()); // Uncompressed TransportData
33///
34/// let string = "This is my rather long string ".repeat(10);
35/// let compressed = TransportData::compress(string.as_bytes()).unwrap();
36/// assert_eq!(string.as_bytes(), compressed.unwrap().unwrap());
37/// ```
38#[derive(Clone)]
39#[cfg_attr(
40    feature = "transport_serde",
41    derive(serde::Serialize, serde::Deserialize)
42)]
43pub struct TransportData {
44    bytes: Bytes,
45    compressed: bool,
46}
47
48impl TransportData {
49    /// Create new [`TransportData`]
50    pub fn new(bytes: &[u8]) -> TransportData {
51        Self {
52            bytes: Bytes::from(bytes),
53            compressed: false,
54        }
55    }
56
57    /// Create a new [`TransportData`] with compression
58    #[cfg(feature = "compress")]
59    pub fn compress(buffer: &[u8]) -> Result<TransportData, TransportDataError> {
60        Self::compress_with_level(buffer, DEFAULT_COMPRESSION_LEVEL)
61    }
62
63    /// Create a new [`TransportData`] with a specific [zstd] compression level
64    #[cfg(feature = "compress")]
65    pub fn compress_with_level(
66        buffer: &[u8],
67        level: i32,
68    ) -> Result<TransportData, TransportDataError> {
69        Ok(Self {
70            bytes: Bytes::from(zstd::encode_all(buffer, level)?),
71            compressed: true,
72        })
73    }
74
75    /// Unwrap the [TransportData], decompressing if necessary.
76    ///
77    /// Always returns [Ok] when `compress` feature is disabled as no decompression will occur.
78    pub fn unwrap(&self) -> Result<Vec<u8>, TransportDataError> {
79        #[cfg(not(feature = "compress"))]
80        {
81            Ok(Vec::from(&*self.bytes))
82        }
83        #[cfg(feature = "compress")]
84        {
85            if self.compressed {
86                Ok(zstd::decode_all(&*self.bytes)?)
87            } else {
88                Ok(Vec::from(&*self.bytes))
89            }
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn basic() -> Result<(), TransportDataError> {
100        let t = TransportData::new("hello".as_bytes());
101
102        let b = t.unwrap()?;
103        assert_eq!(b, "hello".as_bytes());
104
105        Ok(())
106    }
107}