http_body_util/
full.rs

1use bytes::{Buf, Bytes};
2use http_body::{Body, Frame, SizeHint};
3use pin_project_lite::pin_project;
4use std::borrow::Cow;
5use std::convert::{Infallible, TryFrom};
6use std::pin::Pin;
7use std::task::{Context, Poll};
8
9pin_project! {
10    /// A body that consists of a single chunk.
11    #[derive(Clone, Copy, Debug)]
12    pub struct Full<D> {
13        data: Option<D>,
14    }
15}
16
17impl<D> Full<D>
18where
19    D: Buf,
20{
21    /// Create a new `Full`.
22    pub fn new(data: D) -> Self {
23        let data = if data.has_remaining() {
24            Some(data)
25        } else {
26            None
27        };
28        Full { data }
29    }
30}
31
32impl<D> Body for Full<D>
33where
34    D: Buf,
35{
36    type Data = D;
37    type Error = Infallible;
38
39    fn poll_frame(
40        mut self: Pin<&mut Self>,
41        _cx: &mut Context<'_>,
42    ) -> Poll<Option<Result<Frame<D>, Self::Error>>> {
43        Poll::Ready(self.data.take().map(|d| Ok(Frame::data(d))))
44    }
45
46    fn is_end_stream(&self) -> bool {
47        self.data.is_none()
48    }
49
50    fn size_hint(&self) -> SizeHint {
51        self.data
52            .as_ref()
53            .map(|data| SizeHint::with_exact(u64::try_from(data.remaining()).unwrap()))
54            .unwrap_or_else(|| SizeHint::with_exact(0))
55    }
56}
57
58impl<D> Default for Full<D>
59where
60    D: Buf,
61{
62    /// Create an empty `Full`.
63    fn default() -> Self {
64        Full { data: None }
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::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!(
136            full.frame().await.unwrap().unwrap().into_data().unwrap(),
137            &b"hello"[..]
138        );
139        assert!(full.frame().await.is_none());
140    }
141
142    #[tokio::test]
143    async fn empty_full_returns_none() {
144        assert!(Full::<&[u8]>::default().frame().await.is_none());
145        assert!(Full::new(&b""[..]).frame().await.is_none());
146    }
147}