| 1 | use std::error::Error; |
| 2 | use std::fmt::Debug; |
| 3 | use std::pin::Pin; |
| 4 | use std::task::{Context, Poll}; |
| 5 | |
| 6 | use bytes::Buf; |
| 7 | use http_body::{Body, Frame, SizeHint}; |
| 8 | use proj::EitherProj; |
| 9 | |
| 10 | /// Sum type with two cases: [`Left`] and [`Right`], used if a body can be one of |
| 11 | /// two distinct types. |
| 12 | /// |
| 13 | /// [`Left`]: Either::Left |
| 14 | /// [`Right`]: Either::Right |
| 15 | #[derive (Debug, Clone, Copy)] |
| 16 | pub enum Either<L, R> { |
| 17 | /// A value of type `L` |
| 18 | Left(L), |
| 19 | /// A value of type `R` |
| 20 | Right(R), |
| 21 | } |
| 22 | |
| 23 | impl<L, R> Either<L, R> { |
| 24 | /// This function is part of the generated code from `pin-project-lite`, |
| 25 | /// for a more in depth explanation and the rest of the generated code refer |
| 26 | /// to the [`proj`] module. |
| 27 | pub(crate) fn project(self: Pin<&mut Self>) -> EitherProj<L, R> { |
| 28 | unsafe { |
| 29 | match self.get_unchecked_mut() { |
| 30 | Self::Left(left: &mut L) => EitherProj::Left(Pin::new_unchecked(pointer:left)), |
| 31 | Self::Right(right: &mut R) => EitherProj::Right(Pin::new_unchecked(pointer:right)), |
| 32 | } |
| 33 | } |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | impl<L> Either<L, L> { |
| 38 | /// Convert [`Either`] into the inner type, if both `Left` and `Right` are |
| 39 | /// of the same type. |
| 40 | pub fn into_inner(self) -> L { |
| 41 | match self { |
| 42 | Either::Left(left: L) => left, |
| 43 | Either::Right(right: L) => right, |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | impl<L, R, Data> Body for Either<L, R> |
| 49 | where |
| 50 | L: Body<Data = Data>, |
| 51 | R: Body<Data = Data>, |
| 52 | L::Error: Into<Box<dyn Error + Send + Sync>>, |
| 53 | R::Error: Into<Box<dyn Error + Send + Sync>>, |
| 54 | Data: Buf, |
| 55 | { |
| 56 | type Data = Data; |
| 57 | type Error = Box<dyn Error + Send + Sync>; |
| 58 | |
| 59 | fn poll_frame( |
| 60 | self: Pin<&mut Self>, |
| 61 | cx: &mut Context<'_>, |
| 62 | ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> { |
| 63 | match self.project() { |
| 64 | EitherProj::Left(left) => left |
| 65 | .poll_frame(cx) |
| 66 | .map(|poll| poll.map(|opt| opt.map_err(Into::into))), |
| 67 | EitherProj::Right(right) => right |
| 68 | .poll_frame(cx) |
| 69 | .map(|poll| poll.map(|opt| opt.map_err(Into::into))), |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | fn is_end_stream(&self) -> bool { |
| 74 | match self { |
| 75 | Either::Left(left) => left.is_end_stream(), |
| 76 | Either::Right(right) => right.is_end_stream(), |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | fn size_hint(&self) -> SizeHint { |
| 81 | match self { |
| 82 | Either::Left(left) => left.size_hint(), |
| 83 | Either::Right(right) => right.size_hint(), |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | pub(crate) mod proj { |
| 89 | //! This code is the (cleaned output) generated by [pin-project-lite], as it |
| 90 | //! does not support tuple variants. |
| 91 | //! |
| 92 | //! This is the altered expansion from the following snippet, expanded by |
| 93 | //! `cargo-expand`: |
| 94 | //! |
| 95 | //! ```rust |
| 96 | //! use pin_project_lite::pin_project; |
| 97 | //! |
| 98 | //! pin_project! { |
| 99 | //! #[project = EitherProj] |
| 100 | //! pub enum Either<L, R> { |
| 101 | //! Left {#[pin] left: L}, |
| 102 | //! Right {#[pin] right: R} |
| 103 | //! } |
| 104 | //! } |
| 105 | //! ``` |
| 106 | //! |
| 107 | //! [pin-project-lite]: https://docs.rs/pin-project-lite/latest/pin_project_lite/ |
| 108 | use std::marker::PhantomData; |
| 109 | use std::pin::Pin; |
| 110 | |
| 111 | use super::Either; |
| 112 | |
| 113 | #[allow (dead_code)] |
| 114 | #[allow (single_use_lifetimes)] |
| 115 | #[allow (unknown_lints)] |
| 116 | #[allow (clippy::mut_mut)] |
| 117 | #[allow (clippy::redundant_pub_crate)] |
| 118 | #[allow (clippy::ref_option_ref)] |
| 119 | #[allow (clippy::type_repetition_in_bounds)] |
| 120 | pub(crate) enum EitherProj<'__pin, L, R> |
| 121 | where |
| 122 | Either<L, R>: '__pin, |
| 123 | { |
| 124 | Left(Pin<&'__pin mut L>), |
| 125 | Right(Pin<&'__pin mut R>), |
| 126 | } |
| 127 | |
| 128 | #[allow (single_use_lifetimes)] |
| 129 | #[allow (unknown_lints)] |
| 130 | #[allow (clippy::used_underscore_binding)] |
| 131 | #[allow (missing_debug_implementations)] |
| 132 | const _: () = { |
| 133 | #[allow (non_snake_case)] |
| 134 | pub struct __Origin<'__pin, L, R> { |
| 135 | __dummy_lifetime: PhantomData<&'__pin ()>, |
| 136 | _Left: L, |
| 137 | _Right: R, |
| 138 | } |
| 139 | impl<'__pin, L, R> Unpin for Either<L, R> where __Origin<'__pin, L, R>: Unpin {} |
| 140 | |
| 141 | trait MustNotImplDrop {} |
| 142 | #[allow (drop_bounds)] |
| 143 | impl<T: Drop> MustNotImplDrop for T {} |
| 144 | impl<L, R> MustNotImplDrop for Either<L, R> {} |
| 145 | }; |
| 146 | } |
| 147 | |
| 148 | #[cfg (test)] |
| 149 | mod tests { |
| 150 | use super::*; |
| 151 | use crate::{BodyExt, Empty, Full}; |
| 152 | |
| 153 | #[tokio::test] |
| 154 | async fn data_left() { |
| 155 | let full = Full::new(&b"hello" [..]); |
| 156 | |
| 157 | let mut value: Either<_, Empty<&[u8]>> = Either::Left(full); |
| 158 | |
| 159 | assert_eq!(value.size_hint().exact(), Some(b"hello" .len() as u64)); |
| 160 | assert_eq!( |
| 161 | value.frame().await.unwrap().unwrap().into_data().unwrap(), |
| 162 | &b"hello" [..] |
| 163 | ); |
| 164 | assert!(value.frame().await.is_none()); |
| 165 | } |
| 166 | |
| 167 | #[tokio::test] |
| 168 | async fn data_right() { |
| 169 | let full = Full::new(&b"hello!" [..]); |
| 170 | |
| 171 | let mut value: Either<Empty<&[u8]>, _> = Either::Right(full); |
| 172 | |
| 173 | assert_eq!(value.size_hint().exact(), Some(b"hello!" .len() as u64)); |
| 174 | assert_eq!( |
| 175 | value.frame().await.unwrap().unwrap().into_data().unwrap(), |
| 176 | &b"hello!" [..] |
| 177 | ); |
| 178 | assert!(value.frame().await.is_none()); |
| 179 | } |
| 180 | |
| 181 | #[test ] |
| 182 | fn into_inner() { |
| 183 | let a = Either::<i32, i32>::Left(2); |
| 184 | assert_eq!(a.into_inner(), 2) |
| 185 | } |
| 186 | } |
| 187 | |