Skip to main content

hyper_utils/http_body_util/
full.rs

1use std::{
2    borrow::Cow,
3    convert::{Infallible, TryFrom},
4    pin::Pin,
5    task::{Context, Poll},
6};
7
8use bytes::{Buf, Bytes};
9use hyper::body::{Body, Frame, SizeHint};
10use pin_project_lite::pin_project;
11
12pin_project! {
13    /// A body that consists of a single chunk.
14    #[derive(Clone, Copy, Debug)]
15    pub struct Full<D> {
16        data: Option<D>,
17    }
18}
19
20impl<D> Full<D>
21where
22    D: Buf,
23{
24    /// Create a new `Full`.
25    pub fn new(data: D) -> Self {
26        let data = if data.has_remaining() { Some(data) } else { None };
27        Full {
28            data,
29        }
30    }
31}
32
33impl<D> Body for Full<D>
34where
35    D: Buf,
36{
37    type Data = D;
38    type Error = Infallible;
39
40    fn poll_frame(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Result<Frame<D>, Self::Error>>> {
41        Poll::Ready(self.data.take().map(|d| Ok(Frame::data(d))))
42    }
43
44    fn is_end_stream(&self) -> bool {
45        self.data.is_none()
46    }
47
48    fn size_hint(&self) -> SizeHint {
49        self.data
50            .as_ref()
51            .map(|data| SizeHint::with_exact(u64::try_from(data.remaining()).unwrap()))
52            .unwrap_or_else(|| SizeHint::with_exact(0))
53    }
54}
55
56impl<D> Default for Full<D>
57where
58    D: Buf,
59{
60    /// Create an empty `Full`.
61    fn default() -> Self {
62        Full {
63            data: None,
64        }
65    }
66}
67
68impl<D> From<Bytes> for Full<D>
69where
70    D: Buf + From<Bytes>,
71{
72    fn from(bytes: Bytes) -> Self {
73        Full::new(D::from(bytes))
74    }
75}
76
77impl<D> From<Vec<u8>> for Full<D>
78where
79    D: Buf + From<Vec<u8>>,
80{
81    fn from(vec: Vec<u8>) -> Self {
82        Full::new(D::from(vec))
83    }
84}
85
86impl<D> From<&'static [u8]> for Full<D>
87where
88    D: Buf + From<&'static [u8]>,
89{
90    fn from(slice: &'static [u8]) -> Self {
91        Full::new(D::from(slice))
92    }
93}
94
95impl<D, B> From<Cow<'static, B>> for Full<D>
96where
97    D: Buf + From<&'static B> + From<B::Owned>,
98    B: ToOwned + ?Sized,
99{
100    fn from(cow: Cow<'static, B>) -> Self {
101        match cow {
102            Cow::Borrowed(b) => Full::new(D::from(b)),
103            Cow::Owned(o) => Full::new(D::from(o)),
104        }
105    }
106}
107
108impl<D> From<String> for Full<D>
109where
110    D: Buf + From<String>,
111{
112    fn from(s: String) -> Self {
113        Full::new(D::from(s))
114    }
115}
116
117impl<D> From<&'static str> for Full<D>
118where
119    D: Buf + From<&'static str>,
120{
121    fn from(slice: &'static str) -> Self {
122        Full::new(D::from(slice))
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::http_body_util::BodyExt;
130
131    #[tokio::test]
132    async fn full_returns_some() {
133        let mut full = Full::new(&b"hello"[..]);
134        assert_eq!(full.size_hint().exact(), Some(b"hello".len() as u64));
135        assert_eq!(full.frame().await.unwrap().unwrap().into_data().unwrap(), &b"hello"[..]);
136        assert!(full.frame().await.is_none());
137    }
138
139    #[tokio::test]
140    async fn empty_full_returns_none() {
141        assert!(Full::<&[u8]>::default().frame().await.is_none());
142        assert!(Full::new(&b""[..]).frame().await.is_none());
143    }
144}