Skip to content

Commit

Permalink
feat: implement Clone for Request, Response, Extensions (#634)
Browse files Browse the repository at this point in the history
A breaking change, requiring all extensions to `impl Clone`.
  • Loading branch information
seanmonstar committed Nov 10, 2023
1 parent aa1af37 commit f5f31f0
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 10 deletions.
55 changes: 47 additions & 8 deletions src/extensions.rs
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashMap;
use std::fmt;
use std::hash::{BuildHasherDefault, Hasher};

type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
type AnyMap = HashMap<TypeId, Box<dyn AnyClone + Send + Sync>, BuildHasherDefault<IdHasher>>;

// With TypeIds as keys, there's no need to hash them. They are already hashes
// themselves, coming from the compiler. The IdHasher just holds the u64 of
Expand Down Expand Up @@ -31,7 +31,7 @@ impl Hasher for IdHasher {
///
/// `Extensions` can be used by `Request` and `Response` to store
/// extra data derived from the underlying protocol.
#[derive(Default)]
#[derive(Clone, Default)]
pub struct Extensions {
// If extensions are never used, no need to carry around an empty HashMap.
// That's 3 words. Instead, this is only 1 word.
Expand Down Expand Up @@ -59,12 +59,12 @@ impl Extensions {
/// assert!(ext.insert(4u8).is_none());
/// assert_eq!(ext.insert(9i32), Some(5i32));
/// ```
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
pub fn insert<T: Clone + Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
self.map
.get_or_insert_with(|| Box::new(HashMap::default()))
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(|boxed| {
(boxed as Box<dyn Any + 'static>)
boxed.into_any()
.downcast()
.ok()
.map(|boxed| *boxed)
Expand All @@ -87,7 +87,7 @@ impl Extensions {
self.map
.as_ref()
.and_then(|map| map.get(&TypeId::of::<T>()))
.and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref())
.and_then(|boxed| (&**boxed).as_any().downcast_ref())
}

/// Get a mutable reference to a type previously inserted on this `Extensions`.
Expand All @@ -106,7 +106,7 @@ impl Extensions {
self.map
.as_mut()
.and_then(|map| map.get_mut(&TypeId::of::<T>()))
.and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut())
.and_then(|boxed| (&mut **boxed).as_any_mut().downcast_mut())
}

/// Remove a type from this `Extensions`.
Expand All @@ -127,7 +127,7 @@ impl Extensions {
.as_mut()
.and_then(|map| map.remove(&TypeId::of::<T>()))
.and_then(|boxed| {
(boxed as Box<dyn Any + 'static>)
boxed.into_any()
.downcast()
.ok()
.map(|boxed| *boxed)
Expand Down Expand Up @@ -229,9 +229,42 @@ impl fmt::Debug for Extensions {
}
}

trait AnyClone: Any {
fn clone_box(&self) -> Box<dyn AnyClone + Send + Sync>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
}

impl<T: Clone + Send + Sync + 'static> AnyClone for T {
fn clone_box(&self) -> Box<dyn AnyClone + Send + Sync> {
Box::new(self.clone())
}

fn as_any(&self) -> &dyn Any {
self
}

fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}

impl Clone for Box<dyn AnyClone + Send + Sync> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}



#[test]
fn test_extensions() {
#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
struct MyType(i32);

let mut extensions = Extensions::new();
Expand All @@ -242,9 +275,15 @@ fn test_extensions() {
assert_eq!(extensions.get(), Some(&5i32));
assert_eq!(extensions.get_mut(), Some(&mut 5i32));

let ext2 = extensions.clone();

assert_eq!(extensions.remove::<i32>(), Some(5i32));
assert!(extensions.get::<i32>().is_none());

// clone still has it
assert_eq!(ext2.get(), Some(&5i32));
assert_eq!(ext2.get(), Some(&MyType(10)));

assert_eq!(extensions.get::<bool>(), None);
assert_eq!(extensions.get(), Some(&MyType(10)));
}
4 changes: 3 additions & 1 deletion src/request.rs
Expand Up @@ -154,6 +154,7 @@ use crate::{Extensions, Result, Uri};
/// #
/// # fn main() {}
/// ```
#[derive(Clone)]
pub struct Request<T> {
head: Parts,
body: T,
Expand All @@ -163,6 +164,7 @@ pub struct Request<T> {
///
/// The HTTP request head consists of a method, uri, version, and a set of
/// header fields.
#[derive(Clone)]
pub struct Parts {
/// The request's method
pub method: Method,
Expand Down Expand Up @@ -978,7 +980,7 @@ impl Builder {
/// ```
pub fn extension<T>(self, extension: T) -> Builder
where
T: Any + Send + Sync + 'static,
T: Clone + Any + Send + Sync + 'static,
{
self.and_then(move |mut head| {
head.extensions.insert(extension);
Expand Down
4 changes: 3 additions & 1 deletion src/response.rs
Expand Up @@ -176,6 +176,7 @@ use crate::{Extensions, Result};
/// #
/// # fn main() {}
/// ```
#[derive(Clone)]
pub struct Response<T> {
head: Parts,
body: T,
Expand All @@ -185,6 +186,7 @@ pub struct Response<T> {
///
/// The HTTP response head consists of a status, version, and a set of
/// header fields.
#[derive(Clone)]
pub struct Parts {
/// The response's status
pub status: StatusCode,
Expand Down Expand Up @@ -684,7 +686,7 @@ impl Builder {
/// ```
pub fn extension<T>(self, extension: T) -> Builder
where
T: Any + Send + Sync + 'static,
T: Clone + Any + Send + Sync + 'static,
{
self.and_then(move |mut head| {
head.extensions.insert(extension);
Expand Down

0 comments on commit f5f31f0

Please sign in to comment.