twilight_http/response/
mod.rs

1//! Response utility type and related types.
2//!
3//! The heart of the response module is the [`Response`] itself: it's a wrapper
4//! over the underlying HTTP client's response, containing helper methods for
5//! things like [getting the raw bytes][`bytes`] of the response body, getting
6//! an [iterator of the response headers][`headers`], or
7//! [deserializing the body into a model][`model`].
8//!
9//! The [`ResponseFuture`] is a type implementing [`Future`] that resolves to a
10//! [`Response`] when polled or `.await`ed to completion.
11//!
12//! # Examples
13//!
14//! Get a user by ID, check if the request was successful, and if so deserialize
15//! the response body via [`Response::model`][`model`] and print the user's
16//! name:
17//!
18//! ```no_run
19//! # #[tokio::main]
20//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
21//! # let user_id = twilight_model::id::Id::new(1);
22//! use std::env;
23//! use twilight_http::Client;
24//!
25//! let client = Client::new(env::var("DISCORD_TOKEN")?);
26//! let response = client.user(user_id).await?;
27//!
28//! if !response.status().is_success() {
29//!     println!("failed to get user");
30//!
31//!     return Ok(());
32//! }
33//!
34//! // Twilight already knows to deserialize it into a
35//! // `twilight_model::user::User`.
36//! let user = response.model().await?;
37//!
38//! println!("user's name: {}:{}", user.name, user.discriminator);
39//! # Ok(()) }
40//! ```
41//!
42//! [`Future`]: std::future::Future
43//! [`bytes`]: Response::bytes
44//! [`headers`]: Response::headers
45//! [`model`]: Response::model
46
47pub mod marker;
48
49pub(crate) mod future;
50
51mod status_code;
52
53pub use self::{future::ResponseFuture, status_code::StatusCode};
54
55use self::marker::ListBody;
56use http::{
57    header::{HeaderValue, Iter as HeaderMapIter},
58    Response as HyperResponse,
59};
60use http_body_util::BodyExt;
61use hyper::body::{Bytes, Incoming};
62use serde::de::DeserializeOwned;
63use std::{
64    error::Error,
65    fmt::{Display, Formatter, Result as FmtResult},
66    future::Future,
67    iter::FusedIterator,
68    marker::PhantomData,
69    pin::Pin,
70    task::{Context, Poll},
71};
72
73/// Failure when processing a response body.
74#[derive(Debug)]
75pub struct DeserializeBodyError {
76    kind: DeserializeBodyErrorType,
77    source: Option<Box<dyn Error + Send + Sync>>,
78}
79
80impl DeserializeBodyError {
81    /// Immutable reference to the type of error that occurred.
82    #[must_use = "retrieving the type has no effect if left unused"]
83    pub const fn kind(&self) -> &DeserializeBodyErrorType {
84        &self.kind
85    }
86
87    /// Consume the error, returning the source error if there is any.
88    #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
89    pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
90        self.source
91    }
92
93    /// Consume the error, returning the owned error type and the source error.
94    #[must_use = "consuming the error into its parts has no effect if left unused"]
95    pub fn into_parts(
96        self,
97    ) -> (
98        DeserializeBodyErrorType,
99        Option<Box<dyn Error + Send + Sync>>,
100    ) {
101        (self.kind, self.source)
102    }
103}
104
105impl Display for DeserializeBodyError {
106    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
107        match &self.kind {
108            &DeserializeBodyErrorType::BodyNotUtf8 { .. } => {
109                f.write_str("response body is not a utf-8 valid string")
110            }
111            DeserializeBodyErrorType::Chunking { .. } => {
112                f.write_str("failed to chunk response body")
113            }
114            #[cfg(feature = "decompression")]
115            DeserializeBodyErrorType::Decompressing { .. } => {
116                f.write_str("failed to decompress response body")
117            }
118            DeserializeBodyErrorType::Deserializing { .. } => {
119                f.write_str("failed to deserialize response body")
120            }
121        }
122    }
123}
124
125impl Error for DeserializeBodyError {
126    fn source(&self) -> Option<&(dyn Error + 'static)> {
127        self.source
128            .as_ref()
129            .map(|source| &**source as &(dyn Error + 'static))
130    }
131}
132
133/// Type of [`DeserializeBodyError`] that occurred.
134#[derive(Debug)]
135#[non_exhaustive]
136pub enum DeserializeBodyErrorType {
137    /// Response body is not a UTF-8 valid string.
138    BodyNotUtf8 {
139        /// Raw response body bytes that could not be converted into a UTF-8
140        /// valid string.
141        bytes: Vec<u8>,
142    },
143    /// Response body couldn't be chunked.
144    Chunking,
145    /// Decompressing the response failed.
146    #[cfg(feature = "decompression")]
147    Decompressing,
148    /// Deserializing the model failed.
149    Deserializing,
150}
151
152/// Response wrapper containing helper functions over the HTTP client's
153/// response.
154///
155/// This exists so that it can be determined whether to deserialize the body.
156/// This is useful when you don't need the body and therefore don't want to
157/// spend the time to deserialize it.
158///
159/// # Examples
160///
161/// ```no_run
162/// # #[tokio::main]
163/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
164/// # let user_id = twilight_model::id::Id::new(1);
165/// use std::env;
166/// use twilight_http::Client;
167///
168/// let client = Client::new(env::var("DISCORD_TOKEN")?);
169/// let response = client.user(user_id).await?;
170/// println!("status code: {}", response.status());
171///
172/// let user = response.model().await?;
173/// println!("username: {}#{:04}", user.name, user.discriminator);
174/// # Ok(()) }
175/// ```
176#[derive(Debug)]
177pub struct Response<T> {
178    inner: HyperResponse<Incoming>,
179    phantom: PhantomData<T>,
180}
181
182impl<T> Response<T> {
183    pub(crate) const fn new(inner: HyperResponse<Incoming>) -> Self {
184        Self {
185            inner,
186            phantom: PhantomData,
187        }
188    }
189
190    /// Iterator of the response headers.
191    #[must_use = "creating an iterator of the headers has no use on its own"]
192    pub fn headers(&self) -> HeaderIter<'_> {
193        HeaderIter(self.inner.headers().iter())
194    }
195
196    /// Status code of the response.
197    #[must_use = "retrieving the status code has no use on its own"]
198    pub fn status(&self) -> StatusCode {
199        // Convert the `hyper` status code into its raw form in order to return
200        // our own.
201        let raw = self.inner.status().as_u16();
202
203        StatusCode::new(raw)
204    }
205
206    /// Consume the response and accumulate the chunked body into bytes.
207    ///
208    /// For a textual representation of the response body [`text`] should be
209    /// preferred.
210    ///
211    /// # Examples
212    ///
213    /// Count the number of bytes in a response body:
214    ///
215    /// ```no_run
216    /// # #[tokio::main]
217    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
218    /// # let user_id = twilight_model::id::Id::new(1);
219    /// use std::env;
220    /// use twilight_http::Client;
221    ///
222    /// let client = Client::new(env::var("DISCORD_TOKEN")?);
223    /// let response = client.user(user_id).await?;
224    /// let bytes = response.bytes().await?;
225    ///
226    /// println!("size of body: {}", bytes.len());
227    /// # Ok(()) }
228    /// ```
229    ///
230    /// # Errors
231    ///
232    /// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
233    /// response body could not be entirely read.
234    ///
235    /// [`text`]: Self::text
236    pub fn bytes(self) -> BytesFuture {
237        #[cfg(feature = "decompression")]
238        let compressed = self
239            .inner
240            .headers()
241            .get(http::header::CONTENT_ENCODING)
242            .is_some();
243
244        let body = self.inner.into_body();
245
246        let fut = async move {
247            {
248                #[cfg(feature = "decompression")]
249                if compressed {
250                    return decompress(body).await;
251                }
252
253                Ok(body
254                    .collect()
255                    .await
256                    .map_err(|source| DeserializeBodyError {
257                        kind: DeserializeBodyErrorType::Chunking,
258                        source: Some(Box::new(source)),
259                    })?
260                    .to_bytes())
261            }
262        };
263
264        BytesFuture {
265            inner: Box::pin(fut),
266        }
267    }
268
269    /// Consume the response and accumulate the body into a string.
270    ///
271    /// For the raw bytes of the response body [`bytes`] should be preferred.
272    ///
273    /// # Examples
274    ///
275    /// Print the textual response from getting the current user:
276    ///
277    /// ```no_run
278    /// # #[tokio::main]
279    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
280    /// use std::env;
281    /// use twilight_http::Client;
282    ///
283    /// let client = Client::new(env::var("DISCORD_TOKEN")?);
284    /// let response = client.current_user().await?;
285    /// let text = response.text().await?;
286    ///
287    /// println!("body: {text}");
288    /// # Ok(()) }
289    /// ```
290    ///
291    /// # Errors
292    ///
293    /// Returns a [`DeserializeBodyErrorType::BodyNotUtf8`] error type if the
294    /// response body is not UTF-8 valid.
295    ///
296    /// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
297    /// response body could not be entirely read.
298    ///
299    /// [`bytes`]: Self::bytes
300    pub fn text(self) -> TextFuture {
301        TextFuture(self.bytes())
302    }
303}
304
305impl<T: DeserializeOwned> Response<T> {
306    /// Consume the response, chunking the body and then deserializing it into
307    /// the request's matching model.
308    ///
309    /// # Errors
310    ///
311    /// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
312    /// response body could not be entirely read.
313    ///
314    /// Returns a [`DeserializeBodyErrorType::Deserializing`] error type if the
315    /// response body could not be deserialized into the target model.
316    pub fn model(self) -> ModelFuture<T> {
317        ModelFuture::new(self.bytes())
318    }
319}
320
321impl<T: DeserializeOwned> Response<ListBody<T>> {
322    /// Consume the response, chunking the body and then deserializing it into
323    /// a list of something.
324    ///
325    /// This is an alias for [`models`].
326    ///
327    /// # Errors
328    ///
329    /// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
330    /// response body could not be entirely read.
331    ///
332    /// Returns a [`DeserializeBodyErrorType::Deserializing`] error type if the
333    /// response body could not be deserialized into a list of something.
334    ///
335    /// [`models`]: Self::models
336    pub fn model(self) -> ModelFuture<Vec<T>> {
337        self.models()
338    }
339
340    /// Consume the response, chunking the body and then deserializing it into
341    /// a list of something.
342    ///
343    /// # Errors
344    ///
345    /// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
346    /// response body could not be entirely read.
347    ///
348    /// Returns a [`DeserializeBodyErrorType::Deserializing`] error type if the
349    /// response body could not be deserialized into a list of something.
350    pub fn models(self) -> ModelFuture<Vec<T>> {
351        Response::<Vec<T>>::new(self.inner).model()
352    }
353}
354
355/// Iterator over the headers of a [`Response`].
356///
357/// Header names are returned as a string slice and header values are returned
358/// as a slice of bytes. If a header has multiple values then the same header
359/// name may be returned multiple times.
360///
361/// Obtained via [`Response::headers`].
362///
363/// # Examples
364///
365/// Iterate over all of the header names and values of the response from
366/// creating a message:
367///
368/// ```no_run
369/// # #[tokio::main]
370/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
371/// # let channel_id = twilight_model::id::Id::new(1);
372/// use std::env;
373/// use twilight_http::Client;
374///
375/// let client = Client::new(env::var("DISCORD_TOKEN")?);
376/// let response = client.create_message(channel_id)
377//      .content("test")
378///     .await?;
379/// let mut headers = response.headers();
380///
381/// while let Some((name, value)) = headers.next() {
382///     println!("{name}: {}", String::from_utf8_lossy(value));
383/// }
384/// # Ok(()) }
385/// ```
386#[derive(Debug)]
387#[must_use = "iterators do nothing unless used"]
388pub struct HeaderIter<'a>(HeaderMapIter<'a, HeaderValue>);
389
390// `hyper::header::Iter` implements `FusedIterator` so this is a free impl.
391impl FusedIterator for HeaderIter<'_> {}
392
393impl<'a> Iterator for HeaderIter<'a> {
394    // Header names are UTF-8 valid but values aren't, so the value item has
395    // to be a slice of the bytes.
396    type Item = (&'a str, &'a [u8]);
397
398    fn next(&mut self) -> Option<Self::Item> {
399        let (name, value) = self.0.next()?;
400
401        Some((name.as_str(), value.as_bytes()))
402    }
403
404    fn size_hint(&self) -> (usize, Option<usize>) {
405        // `hyper::header::Iter` implements `Iterator::size_hint`.
406        self.0.size_hint()
407    }
408}
409
410/// Future resolving to the bytes of a response body.
411///
412/// The body of the response is chunked and aggregated into a `Vec` of bytes.
413///
414/// Obtained via [`Response::bytes`].
415///
416/// # Examples
417///
418/// Print the bytes of the body of the response from creating a message:
419///
420/// ```no_run
421/// # #[tokio::main]
422/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
423/// # let channel_id = twilight_model::id::Id::new(1);
424/// # let message_id = twilight_model::id::Id::new(2);
425/// use std::env;
426/// use twilight_http::Client;
427///
428/// let client = Client::new(env::var("DISCORD_TOKEN")?);
429/// let response = client.message(channel_id, message_id).await?;
430/// let bytes = response.bytes().await?;
431///
432/// println!("bytes of the body: {bytes:?}");
433/// # Ok(()) }
434/// ```
435///
436/// # Errors
437///
438/// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
439/// response body could not be entirely read.
440#[must_use = "futures do nothing unless you `.await` or poll them"]
441pub struct BytesFuture {
442    inner:
443        Pin<Box<dyn Future<Output = Result<Bytes, DeserializeBodyError>> + Send + Sync + 'static>>,
444}
445
446impl Future for BytesFuture {
447    type Output = Result<Vec<u8>, DeserializeBodyError>;
448
449    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
450        if let Poll::Ready(result) = Pin::new(&mut self.inner).poll(cx) {
451            Poll::Ready(result.map(|b| b.to_vec()))
452        } else {
453            Poll::Pending
454        }
455    }
456}
457
458/// Future resolving to a deserialized model.
459///
460/// Obtained via [`Response::model`].
461///
462/// # Examples
463///
464/// Get an emoji by its ID and print its name:
465///
466/// ```no_run
467/// # #[tokio::main]
468/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
469/// # let guild_id = twilight_model::id::Id::new(1);
470/// # let emoji_id = twilight_model::id::Id::new(2);
471/// use std::env;
472/// use twilight_http::Client;
473///
474/// let client = Client::new(env::var("DISCORD_TOKEN")?);
475/// let emoji = client.emoji(guild_id, emoji_id).await?.model().await?;
476///
477/// println!("emoji name: {}", emoji.name);
478/// # Ok(()) }
479/// ```
480///
481/// # Errors
482///
483/// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
484/// response body could not be entirely read.
485///
486/// Returns a [`DeserializeBodyErrorType::Deserializing`] error type if the
487/// response body could not be deserialized into a model.
488#[must_use = "futures do nothing unless you `.await` or poll them"]
489pub struct ModelFuture<T> {
490    future: BytesFuture,
491    phantom: PhantomData<T>,
492}
493
494impl<T> ModelFuture<T> {
495    const fn new(bytes: BytesFuture) -> Self {
496        Self {
497            future: bytes,
498            phantom: PhantomData,
499        }
500    }
501}
502
503impl<T: DeserializeOwned + Unpin> Future for ModelFuture<T> {
504    type Output = Result<T, DeserializeBodyError>;
505
506    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
507        match Pin::new(&mut self.future).poll(cx) {
508            Poll::Ready(Ok(bytes)) => {
509                Poll::Ready(crate::json::from_bytes(&bytes).map_err(|source| {
510                    DeserializeBodyError {
511                        kind: DeserializeBodyErrorType::Deserializing,
512                        source: Some(Box::new(source)),
513                    }
514                }))
515            }
516            Poll::Ready(Err(source)) => Poll::Ready(Err(source)),
517            Poll::Pending => Poll::Pending,
518        }
519    }
520}
521
522/// Future resolving to the text of a response body.
523///
524/// The body of the response is chunked and aggregated into a string.
525///
526/// Obtained via [`Response::text`].
527///
528/// # Examples
529///
530/// Print the textual body of the response from creating a message:
531///
532/// ```no_run
533/// # #[tokio::main]
534/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
535/// # let channel_id = twilight_model::id::Id::new(1);
536/// # let message_id = twilight_model::id::Id::new(2);
537/// use std::env;
538/// use twilight_http::Client;
539///
540/// let client = Client::new(env::var("DISCORD_TOKEN")?);
541/// let response = client.message(channel_id, message_id).await?;
542/// let text = response.text().await?;
543///
544/// println!("body: {text}");
545/// # Ok(()) }
546/// ```
547///
548/// # Errors
549///
550/// Returns a [`DeserializeBodyErrorType::BodyNotUtf8`] error type if the
551/// response body is not UTF-8 valid.
552///
553/// Returns a [`DeserializeBodyErrorType::Chunking`] error type if the
554/// response body could not be entirely read.
555#[must_use = "futures do nothing unless you `.await` or poll them"]
556pub struct TextFuture(BytesFuture);
557
558impl Future for TextFuture {
559    type Output = Result<String, DeserializeBodyError>;
560
561    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
562        match Pin::new(&mut self.0).poll(cx) {
563            Poll::Ready(Ok(bytes)) => Poll::Ready(String::from_utf8(bytes).map_err(|source| {
564                let utf8_error = source.utf8_error();
565                let bytes = source.into_bytes();
566
567                DeserializeBodyError {
568                    kind: DeserializeBodyErrorType::BodyNotUtf8 { bytes },
569                    source: Some(Box::new(utf8_error)),
570                }
571            })),
572            Poll::Ready(Err(source)) => Poll::Ready(Err(source)),
573            Poll::Pending => Poll::Pending,
574        }
575    }
576}
577
578#[cfg(feature = "decompression")]
579async fn decompress<B: hyper::body::Body>(body: B) -> Result<Bytes, DeserializeBodyError>
580where
581    <B as hyper::body::Body>::Error: Send + Sync + Error + 'static,
582{
583    use brotli_decompressor::Decompressor;
584    use hyper::body::Buf;
585    use std::io::Read;
586
587    let aggregate = body
588        .collect()
589        .await
590        .map_err(|source| DeserializeBodyError {
591            kind: DeserializeBodyErrorType::Chunking,
592            source: Some(Box::new(source)),
593        })?
594        .aggregate();
595
596    // Determine the size of the entire buffer, in order to create the
597    // decompressed and compressed buffers.
598    let size = aggregate.remaining();
599
600    let mut buf = Vec::with_capacity(size);
601
602    Decompressor::new(aggregate.reader(), size)
603        .read_to_end(&mut buf)
604        .map_err(|_| DeserializeBodyError {
605            kind: DeserializeBodyErrorType::Decompressing,
606            source: None,
607        })?;
608
609    Ok(buf.into())
610}
611
612#[cfg(test)]
613mod tests {
614    use super::{
615        marker::{EmptyBody, ListBody},
616        BytesFuture, DeserializeBodyError, DeserializeBodyErrorType, HeaderIter, ModelFuture,
617        Response, TextFuture,
618    };
619    use static_assertions::assert_impl_all;
620    use std::{fmt::Debug, future::Future, iter::FusedIterator};
621    use twilight_model::{channel::Message, guild::Emoji};
622
623    #[cfg(feature = "decompression")]
624    use std::error::Error;
625
626    assert_impl_all!(BytesFuture: Future);
627    assert_impl_all!(DeserializeBodyErrorType: Debug, Send, Sync);
628    assert_impl_all!(DeserializeBodyError: Debug, Send, Sync);
629    assert_impl_all!(HeaderIter<'_>: Debug, FusedIterator, Iterator, Send, Sync);
630    assert_impl_all!(ModelFuture<Emoji>: Future);
631    assert_impl_all!(Response<EmptyBody>: Debug, Send, Sync);
632    assert_impl_all!(Response<ListBody<Message>>: Debug, Send, Sync);
633    assert_impl_all!(TextFuture: Future);
634
635    #[cfg(feature = "decompression")]
636    #[tokio::test]
637    async fn test_decompression() -> Result<(), Box<dyn Error + Send + Sync>> {
638        use super::decompress;
639        use http_body_util::Full;
640        use twilight_model::guild::invite::Invite;
641
642        const COMPRESSED: [u8; 553] = [
643            27, 235, 4, 0, 44, 10, 99, 99, 102, 244, 145, 235, 87, 95, 83, 76, 203, 31, 27, 6, 65,
644            20, 107, 75, 245, 103, 243, 139, 3, 81, 204, 15, 49, 13, 177, 83, 150, 163, 53, 249,
645            217, 44, 58, 93, 125, 117, 56, 81, 249, 9, 5, 129, 64, 112, 146, 109, 175, 185, 252,
646            39, 174, 169, 143, 248, 160, 111, 79, 250, 15, 22, 21, 139, 72, 171, 182, 215, 97, 1,
647            109, 52, 192, 105, 131, 236, 70, 240, 211, 16, 175, 237, 1, 164, 242, 21, 250, 7, 182,
648            87, 200, 84, 121, 177, 139, 184, 62, 86, 239, 221, 212, 206, 23, 176, 184, 173, 182,
649            83, 250, 176, 218, 222, 73, 192, 165, 108, 20, 233, 138, 102, 8, 186, 0, 34, 79, 212,
650            190, 139, 237, 164, 11, 13, 236, 223, 90, 18, 161, 105, 219, 189, 211, 233, 56, 100,
651            27, 53, 61, 230, 220, 103, 22, 220, 157, 206, 198, 33, 124, 46, 160, 49, 72, 66, 109,
652            130, 156, 126, 25, 231, 164, 31, 17, 102, 112, 78, 240, 195, 215, 22, 58, 199, 29, 244,
653            246, 17, 248, 182, 159, 244, 231, 2, 187, 178, 212, 133, 198, 226, 154, 196, 194, 109,
654            105, 237, 98, 73, 70, 73, 174, 133, 214, 16, 22, 165, 73, 132, 37, 25, 78, 185, 13, 20,
655            226, 205, 111, 76, 80, 87, 156, 171, 130, 243, 102, 245, 66, 54, 21, 241, 150, 144,
656            113, 204, 11, 45, 205, 147, 31, 35, 223, 39, 159, 14, 134, 11, 233, 90, 91, 234, 149,
657            220, 63, 225, 191, 155, 78, 23, 26, 233, 239, 12, 87, 75, 185, 112, 53, 5, 218, 162,
658            88, 143, 73, 163, 240, 198, 80, 106, 205, 225, 201, 11, 211, 102, 187, 59, 131, 4, 18,
659            68, 104, 61, 114, 222, 250, 243, 104, 191, 186, 190, 228, 118, 222, 138, 144, 82, 50,
660            65, 20, 233, 128, 139, 237, 52, 175, 75, 228, 168, 57, 75, 2, 210, 98, 28, 86, 21, 106,
661            108, 25, 67, 189, 94, 185, 253, 174, 74, 73, 20, 161, 213, 76, 117, 19, 241, 59, 175,
662            156, 167, 74, 184, 148, 214, 21, 90, 95, 105, 76, 80, 157, 146, 182, 184, 240, 89, 31,
663            94, 80, 68, 218, 177, 126, 147, 26, 184, 109, 211, 32, 123, 49, 11, 120, 16, 190, 124,
664            255, 23, 39, 117, 103, 82, 62, 214, 102, 187, 195, 122, 245, 115, 31, 4, 29, 84, 181,
665            80, 204, 22, 61, 140, 159, 161, 228, 241, 229, 231, 219, 229, 202, 193, 72, 193, 139,
666            151, 179, 135, 40, 217, 140, 251, 3, 18, 106, 142, 249, 255, 73, 62, 156, 133, 5, 28,
667            112, 57, 94, 73, 161, 245, 238, 26, 20, 197, 81, 11, 225, 137, 62, 144, 221, 198, 148,
668            35, 107, 194, 189, 8, 41, 125, 129, 244, 238, 35, 213, 254, 254, 246, 176, 184, 172,
669            112, 85, 54, 235, 239, 79, 250, 151, 27, 34, 79, 149, 124, 0, 103, 230, 132, 251, 122,
670            82, 46, 52, 132, 228, 234, 159, 186, 221, 203, 94, 0, 236, 182, 125, 236, 47, 243, 7,
671            38, 9, 241, 2, 45, 199, 19, 230, 15, 178, 197, 116, 37, 88, 0, 215, 103, 13, 104, 114,
672            248, 15, 240, 7,
673        ];
674
675        let decompressed = decompress(Full::new(COMPRESSED.as_slice())).await?;
676
677        let deserialized = serde_json::from_slice::<Invite>(&decompressed)?;
678
679        assert_eq!(deserialized.code, "twilight-rs");
680
681        Ok(())
682    }
683}