From c5c1553be87b33c36a6de238cb6f52089d852126 Mon Sep 17 00:00:00 2001 From: Lucio Franco Date: Tue, 15 Nov 2022 13:20:45 -0500 Subject: [PATCH] Add `Clone` for Request/Response/Extensions --- Cargo.toml | 5 +++ src/extensions.rs | 97 +++++++++++++++++++++++++---------------------- src/lib.rs | 2 +- src/request.rs | 22 +++-------- src/response.rs | 13 +++---- 5 files changed, 69 insertions(+), 70 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6fe4b13c..a0cd9cce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ bytes = "1" fnv = "1.0.5" itoa = "1" +anymap = "1.0.0-beta.2" + [dev-dependencies] indexmap = "<=1.8" quickcheck = "0.9.0" @@ -62,3 +64,6 @@ path = "benches/method.rs" [[bench]] name = "uri" path = "benches/uri.rs" + +[patch.crates-io] +anymap = { git = "https://github.com/LucioFranco/anymap" } diff --git a/src/extensions.rs b/src/extensions.rs index 7e815df7..b504950c 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -1,9 +1,8 @@ -use std::any::{Any, TypeId}; -use std::collections::HashMap; +use anymap::{CloneAny, Map}; use std::fmt; -use std::hash::{BuildHasherDefault, Hasher}; +use std::hash::Hasher; -type AnyMap = HashMap, BuildHasherDefault>; +type AnyMap = Map; // 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 +30,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(Default, Clone)] 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,16 +58,10 @@ 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) - .downcast() - .ok() - .map(|boxed| *boxed) - }) + .get_or_insert_with(|| Box::new(AnyMap::new())) + .insert(val) } /// Get a reference to a type previously inserted on this `Extensions`. @@ -83,11 +76,8 @@ impl Extensions { /// /// assert_eq!(ext.get::(), Some(&5i32)); /// ``` - pub fn get(&self) -> Option<&T> { - self.map - .as_ref() - .and_then(|map| map.get(&TypeId::of::())) - .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) + pub fn get(&self) -> Option<&T> { + self.map.as_ref().and_then(|map| map.get::()) } /// Get a mutable reference to a type previously inserted on this `Extensions`. @@ -102,11 +92,8 @@ impl Extensions { /// /// assert_eq!(ext.get::().unwrap(), "Hello World"); /// ``` - pub fn get_mut(&mut self) -> Option<&mut T> { - self.map - .as_mut() - .and_then(|map| map.get_mut(&TypeId::of::())) - .and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut()) + pub fn get_mut(&mut self) -> Option<&mut T> { + self.map.as_mut().and_then(|map| map.get_mut::()) } /// Remove a type from this `Extensions`. @@ -122,16 +109,8 @@ impl Extensions { /// assert_eq!(ext.remove::(), Some(5i32)); /// assert!(ext.get::().is_none()); /// ``` - pub fn remove(&mut self) -> Option { - self.map - .as_mut() - .and_then(|map| map.remove(&TypeId::of::())) - .and_then(|boxed| { - (boxed as Box) - .downcast() - .ok() - .map(|boxed| *boxed) - }) + pub fn remove(&mut self) -> Option { + self.map.as_mut().and_then(|map| map.remove::()) } /// Clear the `Extensions` of all inserted extensions. @@ -166,9 +145,7 @@ impl Extensions { /// ``` #[inline] pub fn is_empty(&self) -> bool { - self.map - .as_ref() - .map_or(true, |map| map.is_empty()) + self.map.as_ref().map_or(true, |map| map.is_empty()) } /// Get the numer of extensions available. @@ -184,28 +161,26 @@ impl Extensions { /// ``` #[inline] pub fn len(&self) -> usize { - self.map - .as_ref() - .map_or(0, |map| map.len()) + self.map.as_ref().map_or(0, |map| map.len()) } /// Extends `self` with another `Extensions`. /// /// If an instance of a specific type exists in both, the one in `self` is overwritten with the /// one from `other`. - /// + /// /// # Example - /// + /// /// ``` /// # use http::Extensions; /// let mut ext_a = Extensions::new(); /// ext_a.insert(8u8); /// ext_a.insert(16u16); - /// + /// /// let mut ext_b = Extensions::new(); /// ext_b.insert(4u8); /// ext_b.insert("hello"); - /// + /// /// ext_a.extend(ext_b); /// assert_eq!(ext_a.len(), 3); /// assert_eq!(ext_a.get::(), Some(&4u8)); @@ -229,9 +204,41 @@ impl fmt::Debug for Extensions { } } +/// A newtype that enables non clonable items to be added +/// to the extension map. +#[derive(Debug)] +pub struct NotCloneExtension(Option); + +impl NotCloneExtension { + /// Create a new `NotClone` with `T`. + pub fn new(inner: T) -> Self { + Self(Some(inner)) + } + + /// Get an immutable reference to `T`. + /// + /// Returns None, if this is a clone of the original type. + pub fn get(&self) -> Option<&T> { + self.0.as_ref() + } + + /// Get a mutable reference to `T`. + /// + /// Returns None, if this is a clone of the original type. + pub fn get_mut(&mut self) -> Option<&mut T> { + self.0.as_mut() + } +} + +impl Clone for NotCloneExtension { + fn clone(&self) -> Self { + Self(None) + } +} + #[test] fn test_extensions() { - #[derive(Debug, PartialEq)] + #[derive(Clone, Debug, PartialEq)] struct MyType(i32); let mut extensions = Extensions::new(); diff --git a/src/lib.rs b/src/lib.rs index 42118296..4b2d197f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,7 +183,7 @@ mod error; mod extensions; pub use crate::error::{Error, Result}; -pub use crate::extensions::Extensions; +pub use crate::extensions::{Extensions, NotCloneExtension}; #[doc(no_inline)] pub use crate::header::{HeaderMap, HeaderName, HeaderValue}; pub use crate::method::Method; diff --git a/src/request.rs b/src/request.rs index 2f662e9e..e74aed1e 100644 --- a/src/request.rs +++ b/src/request.rs @@ -53,7 +53,7 @@ //! ``` use std::any::Any; -use std::convert::{TryFrom}; +use std::convert::TryFrom; use std::fmt; use crate::header::{HeaderMap, HeaderName, HeaderValue}; @@ -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, @@ -231,7 +233,6 @@ impl Request<()> { where Uri: TryFrom, >::Error: Into, - { Builder::new().method(Method::GET).uri(uri) } @@ -254,7 +255,6 @@ impl Request<()> { where Uri: TryFrom, >::Error: Into, - { Builder::new().method(Method::PUT).uri(uri) } @@ -277,7 +277,6 @@ impl Request<()> { where Uri: TryFrom, >::Error: Into, - { Builder::new().method(Method::POST).uri(uri) } @@ -300,7 +299,6 @@ impl Request<()> { where Uri: TryFrom, >::Error: Into, - { Builder::new().method(Method::DELETE).uri(uri) } @@ -324,7 +322,6 @@ impl Request<()> { where Uri: TryFrom, >::Error: Into, - { Builder::new().method(Method::OPTIONS).uri(uri) } @@ -347,7 +344,6 @@ impl Request<()> { where Uri: TryFrom, >::Error: Into, - { Builder::new().method(Method::HEAD).uri(uri) } @@ -370,7 +366,6 @@ impl Request<()> { where Uri: TryFrom, >::Error: Into, - { Builder::new().method(Method::CONNECT).uri(uri) } @@ -987,7 +982,7 @@ impl Builder { /// ``` pub fn extension(self, extension: T) -> Builder where - T: Any + Send + Sync + 'static, + T: Any + Clone + Send + Sync + 'static, { self.and_then(move |mut head| { head.extensions.insert(extension); @@ -1051,19 +1046,14 @@ impl Builder { /// .unwrap(); /// ``` pub fn body(self, body: T) -> Result> { - self.inner.map(move |head| { - Request { - head, - body, - } - }) + self.inner.map(move |head| Request { head, body }) } // private fn and_then(self, func: F) -> Self where - F: FnOnce(Parts) -> Result + F: FnOnce(Parts) -> Result, { Builder { inner: self.inner.and_then(func), diff --git a/src/response.rs b/src/response.rs index 7233cdb7..9b7dec46 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, @@ -690,7 +692,7 @@ impl Builder { /// ``` pub fn extension(self, extension: T) -> Builder where - T: Any + Send + Sync + 'static, + T: Any + Clone + Send + Sync + 'static, { self.and_then(move |mut head| { head.extensions.insert(extension); @@ -754,19 +756,14 @@ impl Builder { /// .unwrap(); /// ``` pub fn body(self, body: T) -> Result> { - self.inner.map(move |head| { - Response { - head, - body, - } - }) + self.inner.map(move |head| Response { head, body }) } // private fn and_then(self, func: F) -> Self where - F: FnOnce(Parts) -> Result + F: FnOnce(Parts) -> Result, { Builder { inner: self.inner.and_then(func),