xuko-net 0.6.1

xuko's networking utilities
Documentation
//! [`TransportData`] structure

use xuko_core::bytes::Bytes;

/// The default compression level
#[cfg(feature = "compress")]
pub const DEFAULT_COMPRESSION_LEVEL: i32 = zstd::DEFAULT_COMPRESSION_LEVEL;

/// Error that occurs when using [`TransportData`]
#[derive(Debug, thiserror::Error)]
pub enum TransportDataError {
    /// I/O Error
    #[error("IO Error: {0}")]
    IOError(#[from] std::io::Error),
}

/// [`TransportData`] structure
///
/// The [`TransportData`] structure is intended to transfer a series of bytes across the network.
///
/// The bytes can be optionally compressed if the `compress` feature is
/// enabled in which case the [`TransportData::unwrap`] function will try to
/// decompress the data.
///
/// The compression algorithm used is zstd.
///
/// # Examples
///
/// ```
/// use xuko_net::transport::TransportData;
///
/// TransportData::new("hello".as_bytes()); // Uncompressed TransportData
///
/// let string = "This is my rather long string ".repeat(10);
/// let compressed = TransportData::compress(string.as_bytes()).unwrap();
/// assert_eq!(string.as_bytes(), compressed.unwrap().unwrap());
/// ```
#[derive(Clone)]
#[cfg_attr(
    feature = "transport_serde",
    derive(serde::Serialize, serde::Deserialize)
)]
pub struct TransportData {
    bytes: Bytes,
    compressed: bool,
}

impl TransportData {
    /// Create new [`TransportData`]
    pub fn new(bytes: &[u8]) -> TransportData {
        Self {
            bytes: Bytes::from(bytes),
            compressed: false,
        }
    }

    /// Create a new [`TransportData`] with compression
    #[cfg(feature = "compress")]
    pub fn compress(buffer: &[u8]) -> Result<TransportData, TransportDataError> {
        Self::compress_with_level(buffer, DEFAULT_COMPRESSION_LEVEL)
    }

    /// Create a new [`TransportData`] with a specific [zstd] compression level
    #[cfg(feature = "compress")]
    pub fn compress_with_level(
        buffer: &[u8],
        level: i32,
    ) -> Result<TransportData, TransportDataError> {
        Ok(Self {
            bytes: Bytes::from(zstd::encode_all(buffer, level)?),
            compressed: true,
        })
    }

    /// Unwrap the [TransportData], decompressing if necessary.
    ///
    /// Always returns [Ok] when `compress` feature is disabled as no decompression will occur.
    pub fn unwrap(&self) -> Result<Vec<u8>, TransportDataError> {
        #[cfg(not(feature = "compress"))]
        {
            Ok(Vec::from(&*self.bytes))
        }
        #[cfg(feature = "compress")]
        {
            if self.compressed {
                Ok(zstd::decode_all(&*self.bytes)?)
            } else {
                Ok(Vec::from(&*self.bytes))
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn basic() -> Result<(), TransportDataError> {
        let t = TransportData::new("hello".as_bytes());

        let b = t.unwrap()?;
        assert_eq!(b, "hello".as_bytes());

        Ok(())
    }
}