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 | |