twilight_gateway/
compression.rs

1//! Efficiently decompress Discord gateway events.
2
3use std::{error::Error, fmt};
4
5#[cfg(all(not(feature = "zstd"), feature = "zlib"))]
6mod zlib;
7#[cfg(feature = "zstd")]
8mod zstd;
9
10#[cfg(all(not(feature = "zstd"), feature = "zlib"))]
11pub use zlib::Decompressor;
12#[cfg(feature = "zstd")]
13pub use zstd::Decompressor;
14
15/// Decompressed event buffer.
16const BUFFER_SIZE: usize = 32 * 1024;
17
18/// An operation relating to compression failed.
19#[derive(Debug)]
20pub struct CompressionError {
21    /// Type of error.
22    pub(crate) kind: CompressionErrorType,
23    /// Source error if available.
24    pub(crate) source: Option<Box<dyn Error + Send + Sync>>,
25}
26
27impl CompressionError {
28    /// Immutable reference to the type of error that occurred.
29    #[must_use = "retrieving the type has no effect if left unused"]
30    pub const fn kind(&self) -> &CompressionErrorType {
31        &self.kind
32    }
33
34    /// Consume the error, returning the source error if there is any.
35    #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
36    pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
37        self.source
38    }
39
40    /// Consume the error, returning the owned error type and the source error.
41    #[must_use = "consuming the error into its parts has no effect if left unused"]
42    pub fn into_parts(self) -> (CompressionErrorType, Option<Box<dyn Error + Send + Sync>>) {
43        (self.kind, None)
44    }
45
46    /// Shortcut to create a new error for a not UTF-8 message.
47    pub(crate) fn from_utf8_error(source: std::string::FromUtf8Error) -> Self {
48        Self {
49            kind: CompressionErrorType::NotUtf8,
50            source: Some(Box::new(source)),
51        }
52    }
53
54    /// Shortcut to create a new error for an erroneous status code.
55    #[cfg(feature = "zstd")]
56    pub(crate) fn from_code(code: usize) -> Self {
57        Self {
58            kind: CompressionErrorType::Decompressing,
59            source: Some(zstd_safe::get_error_name(code).into()),
60        }
61    }
62
63    /// Shortcut to create a new error for a zlib decompression error.
64    #[cfg(all(not(feature = "zstd"), feature = "zlib"))]
65    pub(crate) fn from_decompress(source: flate2::DecompressError) -> Self {
66        Self {
67            kind: CompressionErrorType::Decompressing,
68            source: Some(source.into()),
69        }
70    }
71}
72
73impl fmt::Display for CompressionError {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self.kind {
76            CompressionErrorType::Decompressing => f.write_str("message could not be decompressed"),
77            CompressionErrorType::NotUtf8 => f.write_str("decompressed message is not UTF-8"),
78        }
79    }
80}
81
82impl Error for CompressionError {
83    fn source(&self) -> Option<&(dyn Error + 'static)> {
84        self.source
85            .as_ref()
86            .map(|source| &**source as &(dyn Error + 'static))
87    }
88}
89
90/// Type of [`CompressionError`] that occurred.
91#[derive(Debug)]
92#[non_exhaustive]
93pub enum CompressionErrorType {
94    /// Decompressing a frame failed.
95    Decompressing,
96    /// Decompressed message is not UTF-8.
97    NotUtf8,
98}