feat(client): allow Connected::extra to be chained to connectors can be composed
This commit is contained in:
		| @@ -267,7 +267,11 @@ impl Connected { | |||||||
|  |  | ||||||
|     /// Set extra connection information to be set in the extensions of every `Response`. |     /// Set extra connection information to be set in the extensions of every `Response`. | ||||||
|     pub fn extra<T: Clone + Send + Sync + 'static>(mut self, extra: T) -> Connected { |     pub fn extra<T: Clone + Send + Sync + 'static>(mut self, extra: T) -> Connected { | ||||||
|  |         if let Some(prev) = self.extra { | ||||||
|  |             self.extra = Some(Extra(Box::new(ExtraChain(prev.0, extra)))); | ||||||
|  |         } else { | ||||||
|             self.extra = Some(Extra(Box::new(ExtraEnvelope(extra)))); |             self.extra = Some(Extra(Box::new(ExtraEnvelope(extra)))); | ||||||
|  |         } | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -311,17 +315,17 @@ impl fmt::Debug for Extra { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | trait ExtraInner: Send + Sync { | ||||||
|  |     fn clone_box(&self) -> Box<ExtraInner>; | ||||||
|  |     fn set(&self, res: &mut Response<::Body>); | ||||||
|  | } | ||||||
|  |  | ||||||
| // This indirection allows the `Connected` to have a type-erased "extra" value, | // This indirection allows the `Connected` to have a type-erased "extra" value, | ||||||
| // while that type still knows its inner extra type. This allows the correct | // while that type still knows its inner extra type. This allows the correct | ||||||
| // TypeId to be used when inserting into `res.extensions_mut()`. | // TypeId to be used when inserting into `res.extensions_mut()`. | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| struct ExtraEnvelope<T>(T); | struct ExtraEnvelope<T>(T); | ||||||
|  |  | ||||||
| trait ExtraInner: Send + Sync { |  | ||||||
|     fn clone_box(&self) -> Box<ExtraInner>; |  | ||||||
|     fn set(&self, res: &mut Response<::Body>); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T> ExtraInner for ExtraEnvelope<T> | impl<T> ExtraInner for ExtraEnvelope<T> | ||||||
| where | where | ||||||
|     T: Clone + Send + Sync + 'static |     T: Clone + Send + Sync + 'static | ||||||
| @@ -331,14 +335,35 @@ where | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn set(&self, res: &mut Response<::Body>) { |     fn set(&self, res: &mut Response<::Body>) { | ||||||
|         let extra = self.0.clone(); |         res.extensions_mut().insert(self.0.clone()); | ||||||
|         res.extensions_mut().insert(extra); |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct ExtraChain<T>(Box<ExtraInner>, T); | ||||||
|  |  | ||||||
|  | impl<T: Clone> Clone for ExtraChain<T> { | ||||||
|  |     fn clone(&self) -> Self { | ||||||
|  |         ExtraChain(self.0.clone_box(), self.1.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T> ExtraInner for ExtraChain<T> | ||||||
|  | where | ||||||
|  |     T: Clone + Send + Sync + 'static | ||||||
|  | { | ||||||
|  |     fn clone_box(&self) -> Box<ExtraInner> { | ||||||
|  |         Box::new(self.clone()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn set(&self, res: &mut Response<::Body>) { | ||||||
|  |         self.0.set(res); | ||||||
|  |         res.extensions_mut().insert(self.1.clone()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use super::Destination; |     use super::{Connected, Destination}; | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_destination_set_scheme() { |     fn test_destination_set_scheme() { | ||||||
| @@ -483,5 +508,76 @@ mod tests { | |||||||
|         assert_eq!(dst.host(), "hyper.rs"); |         assert_eq!(dst.host(), "hyper.rs"); | ||||||
|         assert_eq!(dst.port(), None); |         assert_eq!(dst.port(), None); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[derive(Clone, Debug, PartialEq)] | ||||||
|  |     struct Ex1(usize); | ||||||
|  |  | ||||||
|  |     #[derive(Clone, Debug, PartialEq)] | ||||||
|  |     struct Ex2(&'static str); | ||||||
|  |  | ||||||
|  |     #[derive(Clone, Debug, PartialEq)] | ||||||
|  |     struct Ex3(&'static str); | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_connected_extra() { | ||||||
|  |         let c1 = Connected::new() | ||||||
|  |             .extra(Ex1(41)); | ||||||
|  |  | ||||||
|  |         let mut res1 = ::Response::new(::Body::empty()); | ||||||
|  |  | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex1>(), None); | ||||||
|  |  | ||||||
|  |         c1 | ||||||
|  |             .extra | ||||||
|  |             .as_ref() | ||||||
|  |             .expect("c1 extra") | ||||||
|  |             .set(&mut res1); | ||||||
|  |  | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(41))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_connected_extra_chain() { | ||||||
|  |         // If a user composes connectors and at each stage, there's "extra" | ||||||
|  |         // info to attach, it shouldn't override the previous extras. | ||||||
|  |  | ||||||
|  |         let c1 = Connected::new() | ||||||
|  |             .extra(Ex1(45)) | ||||||
|  |             .extra(Ex2("zoom")) | ||||||
|  |             .extra(Ex3("pew pew")); | ||||||
|  |  | ||||||
|  |         let mut res1 = ::Response::new(::Body::empty()); | ||||||
|  |  | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex1>(), None); | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex2>(), None); | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex3>(), None); | ||||||
|  |  | ||||||
|  |         c1 | ||||||
|  |             .extra | ||||||
|  |             .as_ref() | ||||||
|  |             .expect("c1 extra") | ||||||
|  |             .set(&mut res1); | ||||||
|  |  | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(45))); | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex2>(), Some(&Ex2("zoom"))); | ||||||
|  |         assert_eq!(res1.extensions().get::<Ex3>(), Some(&Ex3("pew pew"))); | ||||||
|  |  | ||||||
|  |         // Just like extensions, inserting the same type overrides previous type. | ||||||
|  |         let c2 = Connected::new() | ||||||
|  |             .extra(Ex1(33)) | ||||||
|  |             .extra(Ex2("hiccup")) | ||||||
|  |             .extra(Ex1(99)); | ||||||
|  |  | ||||||
|  |         let mut res2 = ::Response::new(::Body::empty()); | ||||||
|  |  | ||||||
|  |         c2 | ||||||
|  |             .extra | ||||||
|  |             .as_ref() | ||||||
|  |             .expect("c2 extra") | ||||||
|  |             .set(&mut res2); | ||||||
|  |  | ||||||
|  |         assert_eq!(res2.extensions().get::<Ex1>(), Some(&Ex1(99))); | ||||||
|  |         assert_eq!(res2.extensions().get::<Ex2>(), Some(&Ex2("hiccup"))); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user