Skip to content

Commit f5f31f0

Browse files
authored
feat: implement Clone for Request, Response, Extensions (#634)
A breaking change, requiring all extensions to `impl Clone`.
1 parent aa1af37 commit f5f31f0

File tree

3 files changed

+53
-10
lines changed

3 files changed

+53
-10
lines changed

src/extensions.rs

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::HashMap;
33
use std::fmt;
44
use std::hash::{BuildHasherDefault, Hasher};
55

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

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

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

112112
/// Remove a type from this `Extensions`.
@@ -127,7 +127,7 @@ impl Extensions {
127127
.as_mut()
128128
.and_then(|map| map.remove(&TypeId::of::<T>()))
129129
.and_then(|boxed| {
130-
(boxed as Box<dyn Any + 'static>)
130+
boxed.into_any()
131131
.downcast()
132132
.ok()
133133
.map(|boxed| *boxed)
@@ -229,9 +229,42 @@ impl fmt::Debug for Extensions {
229229
}
230230
}
231231

232+
trait AnyClone: Any {
233+
fn clone_box(&self) -> Box<dyn AnyClone + Send + Sync>;
234+
fn as_any(&self) -> &dyn Any;
235+
fn as_any_mut(&mut self) -> &mut dyn Any;
236+
fn into_any(self: Box<Self>) -> Box<dyn Any>;
237+
}
238+
239+
impl<T: Clone + Send + Sync + 'static> AnyClone for T {
240+
fn clone_box(&self) -> Box<dyn AnyClone + Send + Sync> {
241+
Box::new(self.clone())
242+
}
243+
244+
fn as_any(&self) -> &dyn Any {
245+
self
246+
}
247+
248+
fn as_any_mut(&mut self) -> &mut dyn Any {
249+
self
250+
}
251+
252+
fn into_any(self: Box<Self>) -> Box<dyn Any> {
253+
self
254+
}
255+
}
256+
257+
impl Clone for Box<dyn AnyClone + Send + Sync> {
258+
fn clone(&self) -> Self {
259+
(**self).clone_box()
260+
}
261+
}
262+
263+
264+
232265
#[test]
233266
fn test_extensions() {
234-
#[derive(Debug, PartialEq)]
267+
#[derive(Clone, Debug, PartialEq)]
235268
struct MyType(i32);
236269

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

278+
let ext2 = extensions.clone();
279+
245280
assert_eq!(extensions.remove::<i32>(), Some(5i32));
246281
assert!(extensions.get::<i32>().is_none());
247282

283+
// clone still has it
284+
assert_eq!(ext2.get(), Some(&5i32));
285+
assert_eq!(ext2.get(), Some(&MyType(10)));
286+
248287
assert_eq!(extensions.get::<bool>(), None);
249288
assert_eq!(extensions.get(), Some(&MyType(10)));
250289
}

src/request.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ use crate::{Extensions, Result, Uri};
154154
/// #
155155
/// # fn main() {}
156156
/// ```
157+
#[derive(Clone)]
157158
pub struct Request<T> {
158159
head: Parts,
159160
body: T,
@@ -163,6 +164,7 @@ pub struct Request<T> {
163164
///
164165
/// The HTTP request head consists of a method, uri, version, and a set of
165166
/// header fields.
167+
#[derive(Clone)]
166168
pub struct Parts {
167169
/// The request's method
168170
pub method: Method,
@@ -978,7 +980,7 @@ impl Builder {
978980
/// ```
979981
pub fn extension<T>(self, extension: T) -> Builder
980982
where
981-
T: Any + Send + Sync + 'static,
983+
T: Clone + Any + Send + Sync + 'static,
982984
{
983985
self.and_then(move |mut head| {
984986
head.extensions.insert(extension);

src/response.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ use crate::{Extensions, Result};
176176
/// #
177177
/// # fn main() {}
178178
/// ```
179+
#[derive(Clone)]
179180
pub struct Response<T> {
180181
head: Parts,
181182
body: T,
@@ -185,6 +186,7 @@ pub struct Response<T> {
185186
///
186187
/// The HTTP response head consists of a status, version, and a set of
187188
/// header fields.
189+
#[derive(Clone)]
188190
pub struct Parts {
189191
/// The response's status
190192
pub status: StatusCode,
@@ -684,7 +686,7 @@ impl Builder {
684686
/// ```
685687
pub fn extension<T>(self, extension: T) -> Builder
686688
where
687-
T: Any + Send + Sync + 'static,
689+
T: Clone + Any + Send + Sync + 'static,
688690
{
689691
self.and_then(move |mut head| {
690692
head.extensions.insert(extension);

0 commit comments

Comments
 (0)