From c4c634b0dc4e4b0d115e3d7f5635bc6890cf7a7a Mon Sep 17 00:00:00 2001 From: Daiki Mizukami Date: Wed, 24 Mar 2021 22:06:10 +0900 Subject: [PATCH 1/3] Add `Full` --- Cargo.toml | 3 + src/full.rs | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 3 files changed, 162 insertions(+) create mode 100644 src/full.rs diff --git a/Cargo.toml b/Cargo.toml index ad1e73a..ef1b598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,6 @@ categories = ["web-programming"] bytes = "1" http = "0.2" pin-project-lite = "0.2" + +[dev-dependencies] +tokio = { version = "1", features = ["macros", "rt"] } diff --git a/src/full.rs b/src/full.rs new file mode 100644 index 0000000..e24842b --- /dev/null +++ b/src/full.rs @@ -0,0 +1,157 @@ +use crate::{Body, SizeHint}; +use bytes::{Buf, Bytes}; +use http::HeaderMap; +use std::borrow::Cow; +use std::convert::{Infallible, TryFrom}; +use std::marker::Unpin; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// A body that consists of a single chunk. +#[derive(Clone, Copy, Debug)] +pub struct Full { + data: Option, +} + +impl Full +where + D: Buf + Unpin, +{ + /// Create a new `Full`. + pub fn new(data: D) -> Self { + let data = if data.has_remaining() { + Some(data) + } else { + None + }; + Full { data } + } +} + +impl Body for Full +where + D: Buf + Unpin, +{ + type Data = D; + type Error = Infallible; + + fn poll_data( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll>> { + Poll::Ready(self.data.take().map(Ok)) + } + + fn poll_trailers( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + Poll::Ready(Ok(None)) + } + + fn is_end_stream(&self) -> bool { + self.data.is_none() + } + + fn size_hint(&self) -> SizeHint { + self.data + .as_ref() + .map(|data| { + u64::try_from(data.remaining()) + .map(SizeHint::with_exact) + .unwrap_or_else(|_| { + let mut hint = SizeHint::new(); + hint.set_lower(u64::MAX); + hint + }) + }) + .unwrap_or_else(|| SizeHint::with_exact(0)) + } +} + +impl Default for Full +where + D: Buf + Unpin, +{ + /// Create an empty `Full`. + fn default() -> Self { + Full { data: None } + } +} + +impl From for Full +where + D: Buf + From + Unpin, +{ + fn from(bytes: Bytes) -> Self { + Full::new(D::from(bytes)) + } +} + +impl From> for Full +where + D: Buf + From> + Unpin, +{ + fn from(vec: Vec) -> Self { + Full::new(D::from(vec)) + } +} + +impl From<&'static [u8]> for Full +where + D: Buf + From<&'static [u8]> + Unpin, +{ + fn from(slice: &'static [u8]) -> Self { + Full::new(D::from(slice)) + } +} + +impl From> for Full +where + D: Buf + From<&'static B> + From + Unpin, + B: ToOwned + ?Sized, +{ + fn from(cow: Cow<'static, B>) -> Self { + match cow { + Cow::Borrowed(b) => Full::new(D::from(b)), + Cow::Owned(o) => Full::new(D::from(o)), + } + } +} + +impl From for Full +where + D: Buf + From + Unpin, +{ + fn from(s: String) -> Self { + Full::new(D::from(s)) + } +} + +impl From<&'static str> for Full +where + D: Buf + From<&'static str> + Unpin, +{ + fn from(slice: &'static str) -> Self { + Full::new(D::from(slice)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn full_returns_some() { + let mut full = Full::new(&b"hello"[..]); + assert_eq!(full.size_hint().exact(), Some(b"hello".len() as u64)); + assert_eq!(full.data().await, Some(Ok(&b"hello"[..]))); + assert!(full.data().await.is_none()); + } + + #[tokio::test] + async fn empty_full_returns_none() { + assert!(Full::<&[u8]>::default().data().await.is_none()); + assert!(Full::new(&b""[..]).data().await.is_none()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 488689c..28e4763 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,12 +14,14 @@ //! [`Body`]: trait.Body.html mod empty; +mod full; mod next; mod size_hint; pub mod combinators; pub use self::empty::Empty; +pub use self::full::Full; pub use self::next::{Data, Trailers}; pub use self::size_hint::SizeHint; From 8539a918b3851f9880c48ab41a2fb28c99054d6b Mon Sep 17 00:00:00 2001 From: Daiki Mizukami Date: Fri, 26 Mar 2021 15:32:33 +0900 Subject: [PATCH 2/3] Remove `Unpin` bounds on `Full` impls --- src/full.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/full.rs b/src/full.rs index e24842b..cd20435 100644 --- a/src/full.rs +++ b/src/full.rs @@ -1,21 +1,23 @@ use crate::{Body, SizeHint}; use bytes::{Buf, Bytes}; use http::HeaderMap; +use pin_project_lite::pin_project; use std::borrow::Cow; use std::convert::{Infallible, TryFrom}; -use std::marker::Unpin; use std::pin::Pin; use std::task::{Context, Poll}; -/// A body that consists of a single chunk. -#[derive(Clone, Copy, Debug)] -pub struct Full { - data: Option, +pin_project! { + /// A body that consists of a single chunk. + #[derive(Clone, Copy, Debug)] + pub struct Full { + data: Option, + } } impl Full where - D: Buf + Unpin, + D: Buf, { /// Create a new `Full`. pub fn new(data: D) -> Self { @@ -30,7 +32,7 @@ where impl Body for Full where - D: Buf + Unpin, + D: Buf, { type Data = D; type Error = Infallible; @@ -71,7 +73,7 @@ where impl Default for Full where - D: Buf + Unpin, + D: Buf, { /// Create an empty `Full`. fn default() -> Self { @@ -81,7 +83,7 @@ where impl From for Full where - D: Buf + From + Unpin, + D: Buf + From, { fn from(bytes: Bytes) -> Self { Full::new(D::from(bytes)) @@ -90,7 +92,7 @@ where impl From> for Full where - D: Buf + From> + Unpin, + D: Buf + From>, { fn from(vec: Vec) -> Self { Full::new(D::from(vec)) @@ -99,7 +101,7 @@ where impl From<&'static [u8]> for Full where - D: Buf + From<&'static [u8]> + Unpin, + D: Buf + From<&'static [u8]>, { fn from(slice: &'static [u8]) -> Self { Full::new(D::from(slice)) @@ -108,7 +110,7 @@ where impl From> for Full where - D: Buf + From<&'static B> + From + Unpin, + D: Buf + From<&'static B> + From, B: ToOwned + ?Sized, { fn from(cow: Cow<'static, B>) -> Self { @@ -121,7 +123,7 @@ where impl From for Full where - D: Buf + From + Unpin, + D: Buf + From, { fn from(s: String) -> Self { Full::new(D::from(s)) @@ -130,7 +132,7 @@ where impl From<&'static str> for Full where - D: Buf + From<&'static str> + Unpin, + D: Buf + From<&'static str>, { fn from(slice: &'static str) -> Self { Full::new(D::from(slice)) From 9df31b2f36d0e1caef2f96f88f2a8bfc741bc401 Mon Sep 17 00:00:00 2001 From: Daiki Mizukami Date: Fri, 26 Mar 2021 21:56:53 +0900 Subject: [PATCH 3/3] Simplify the conversion from `usize` to `u64` --- src/full.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/full.rs b/src/full.rs index cd20435..f1d063b 100644 --- a/src/full.rs +++ b/src/full.rs @@ -58,15 +58,7 @@ where fn size_hint(&self) -> SizeHint { self.data .as_ref() - .map(|data| { - u64::try_from(data.remaining()) - .map(SizeHint::with_exact) - .unwrap_or_else(|_| { - let mut hint = SizeHint::new(); - hint.set_lower(u64::MAX); - hint - }) - }) + .map(|data| SizeHint::with_exact(u64::try_from(data.remaining()).unwrap())) .unwrap_or_else(|| SizeHint::with_exact(0)) } }