1use std::error::Error;
2use std::fmt::Debug;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use bytes::Buf;
7use http_body::{Body, Frame, SizeHint};
8use 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)]
16pub enum Either<L, R> {
17 /// A value of type `L`
18 Left(L),
19 /// A value of type `R`
20 Right(R),
21}
22
23impl<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
37impl<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
48impl<L, R, Data> Body for Either<L, R>
49where
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
88pub(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)]
149mod 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