From f5f31f0651774a5d68f2d5051b70e0bd0ccae577 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Fri, 10 Nov 2023 16:35:29 -0500 Subject: [PATCH] feat: implement Clone for Request, Response, Extensions (#634) A breaking change, requiring all extensions to `impl Clone`. --- src/extensions.rs | 55 ++++++++++++++++++++++++++++++++++++++++------- src/request.rs | 4 +++- src/response.rs | 4 +++- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/extensions.rs b/src/extensions.rs index 7e815df7..71a72134 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fmt; use std::hash::{BuildHasherDefault, Hasher}; -type AnyMap = HashMap, BuildHasherDefault>; +type AnyMap = HashMap, BuildHasherDefault>; // 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 @@ -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. @@ -59,12 +59,12 @@ impl Extensions { /// assert!(ext.insert(4u8).is_none()); /// assert_eq!(ext.insert(9i32), Some(5i32)); /// ``` - pub fn insert(&mut self, val: T) -> Option { + pub fn insert(&mut self, val: T) -> Option { self.map .get_or_insert_with(|| Box::new(HashMap::default())) .insert(TypeId::of::(), Box::new(val)) .and_then(|boxed| { - (boxed as Box) + boxed.into_any() .downcast() .ok() .map(|boxed| *boxed) @@ -87,7 +87,7 @@ impl Extensions { self.map .as_ref() .and_then(|map| map.get(&TypeId::of::())) - .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`. @@ -106,7 +106,7 @@ impl Extensions { self.map .as_mut() .and_then(|map| map.get_mut(&TypeId::of::())) - .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`. @@ -127,7 +127,7 @@ impl Extensions { .as_mut() .and_then(|map| map.remove(&TypeId::of::())) .and_then(|boxed| { - (boxed as Box) + boxed.into_any() .downcast() .ok() .map(|boxed| *boxed) @@ -229,9 +229,42 @@ impl fmt::Debug for Extensions { } } +trait AnyClone: Any { + fn clone_box(&self) -> Box; + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; + fn into_any(self: Box) -> Box; +} + +impl AnyClone for T { + fn clone_box(&self) -> Box { + 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) -> Box { + self + } +} + +impl Clone for Box { + 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(); @@ -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::(), Some(5i32)); assert!(extensions.get::().is_none()); + // clone still has it + assert_eq!(ext2.get(), Some(&5i32)); + assert_eq!(ext2.get(), Some(&MyType(10))); + assert_eq!(extensions.get::(), None); assert_eq!(extensions.get(), Some(&MyType(10))); } diff --git a/src/request.rs b/src/request.rs index 4481187c..ed0f673c 100644 --- a/src/request.rs +++ b/src/request.rs @@ -154,6 +154,7 @@ use crate::{Extensions, Result, Uri}; /// # /// # fn main() {} /// ``` +#[derive(Clone)] pub struct Request { head: Parts, body: T, @@ -163,6 +164,7 @@ pub struct Request { /// /// 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, @@ -978,7 +980,7 @@ impl Builder { /// ``` pub fn extension(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); diff --git a/src/response.rs b/src/response.rs index da0fec98..e0136a0f 100644 --- a/src/response.rs +++ b/src/response.rs @@ -176,6 +176,7 @@ use crate::{Extensions, Result}; /// # /// # fn main() {} /// ``` +#[derive(Clone)] pub struct Response { head: Parts, body: T, @@ -185,6 +186,7 @@ pub struct Response { /// /// 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, @@ -684,7 +686,7 @@ impl Builder { /// ``` pub fn extension(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);