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#[derive(Debug, Clone, Copy)]
16pub enum Either<L, R> {
17 Left(L),
19 Right(R),
21}
22
23impl<L, R> Either<L, R> {
24 pub(crate) fn project(self: Pin<&mut Self>) -> EitherProj<L, R> {
28 unsafe {
29 match self.get_unchecked_mut() {
30 Self::Left(left) => EitherProj::Left(Pin::new_unchecked(left)),
31 Self::Right(right) => EitherProj::Right(Pin::new_unchecked(right)),
32 }
33 }
34 }
35}
36
37impl<L> Either<L, L> {
38 pub fn into_inner(self) -> L {
41 match self {
42 Either::Left(left) => left,
43 Either::Right(right) => 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 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}