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 {
|
||||||
self.extra = Some(Extra(Box::new(ExtraEnvelope(extra))));
|
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
|
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