From ec32836f4814421fdfca8c50402cd56cf03d2496 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Tue, 25 Jul 2017 11:24:17 -0700 Subject: [PATCH] add Extensions (#40) --- src/extensions.rs | 157 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 13 ++++ src/request.rs | 40 ++++++++++-- src/response.rs | 38 +++++++++-- 4 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 src/extensions.rs diff --git a/src/extensions.rs b/src/extensions.rs new file mode 100644 index 0000000..87bd443 --- /dev/null +++ b/src/extensions.rs @@ -0,0 +1,157 @@ +use std::any::{Any, TypeId}; +use std::collections::HashMap; +use std::hash::BuildHasherDefault; +use std::fmt; + +use fnv::FnvHasher; + +type AnyMap = HashMap, BuildHasherDefault>; + +/// A type map of protocol extensions. +/// +/// `Extensions` can be used by `Request` and `Response` to store +/// extra data derived from the underlying protocol. +#[derive(Default)] +pub struct Extensions { + map: AnyMap, +} + +impl Extensions { + /// Create an empty `Extensions`. + #[inline] + pub fn new() -> Extensions { + Extensions { + map: HashMap::default(), + } + } + + /// Insert a type into this `Extensions`. + /// + /// If a extension of this type already existed, it will + /// be returned. + /// + /// # Example + /// + /// ``` + /// # use http::Extensions; + /// let mut ext = Extensions::new(); + /// assert!(ext.insert(5i32).is_none()); + /// assert!(ext.insert(4u8).is_none()); + /// assert_eq!(ext.insert(9i32), Some(5i32)); + /// ``` + pub fn insert(&mut self, val: T) -> Option { + self.map.insert(TypeId::of::(), Box::new(val)) + .and_then(|boxed| { + //TODO: we can use unsafe and remove double checking the type id + (boxed as Box) + .downcast() + .ok() + .map(|boxed| *boxed) + }) + } + + /// Get a reference to a type previously inserted on this `Extensions`. + /// + /// # Example + /// + /// ``` + /// # use http::Extensions; + /// let mut ext = Extensions::new(); + /// assert!(ext.get::().is_none()); + /// ext.insert(5i32); + /// + /// assert_eq!(ext.get::(), Some(&5i32)); + /// ``` + pub fn get(&self) -> Option<&T> { + self.map.get(&TypeId::of::()) + //TODO: we can use unsafe and remove double checking the type id + .and_then(|boxed| (&**boxed as &(Any + 'static)).downcast_ref()) + } + + /// Get a mutable reference to a type previously inserted on this `Extensions`. + /// + /// # Example + /// + /// ``` + /// # use http::Extensions; + /// let mut ext = Extensions::new(); + /// ext.insert(String::from("Hello")); + /// ext.get_mut::().unwrap().push_str(" World"); + /// + /// assert_eq!(ext.get::().unwrap(), "Hello World"); + /// ``` + pub fn get_mut(&mut self) -> Option<&mut T> { + self.map.get_mut(&TypeId::of::()) + //TODO: we can use unsafe and remove double checking the type id + .and_then(|boxed| (&mut **boxed as &mut (Any + 'static)).downcast_mut()) + } + + + /// Remove a type from this `Extensions`. + /// + /// If a extension of this type existed, it will be returned. + /// + /// # Example + /// + /// ``` + /// # use http::Extensions; + /// let mut ext = Extensions::new(); + /// ext.insert(5i32); + /// assert_eq!(ext.remove::(), Some(5i32)); + /// assert!(ext.get::().is_none()); + /// ``` + pub fn remove(&mut self) -> Option { + self.map.remove(&TypeId::of::()) + .and_then(|boxed| { + //TODO: we can use unsafe and remove double checking the type id + (boxed as Box) + .downcast() + .ok() + .map(|boxed| *boxed) + }) + } + + /// Clear the `Extensions` of all inserted extensions. + /// + /// # Example + /// + /// ``` + /// # use http::Extensions; + /// let mut ext = Extensions::new(); + /// ext.insert(5i32); + /// ext.clear(); + /// + /// assert!(ext.get::().is_none()); + /// ``` + #[inline] + pub fn clear(&mut self) { + self.map.clear(); + } +} + +impl fmt::Debug for Extensions { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Extensions") + .finish() + } +} + +#[test] +fn test_extensions() { + #[derive(Debug, PartialEq)] + struct MyType(i32); + + let mut extensions = Extensions::new(); + + extensions.insert(5i32); + extensions.insert(MyType(10)); + + assert_eq!(extensions.get(), Some(&5i32)); + assert_eq!(extensions.get_mut(), Some(&mut 5i32)); + + assert_eq!(extensions.remove::(), Some(5i32)); + assert!(extensions.get::().is_none()); + + assert_eq!(extensions.get::(), None); + assert_eq!(extensions.get(), Some(&MyType(10))); +} diff --git a/src/lib.rs b/src/lib.rs index 98a752d..7602b74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,9 @@ pub mod version; pub mod uri; mod byte_str; +mod extensions; +pub use extensions::Extensions; pub use header::HeaderMap; pub use method::Method; pub use request::Request; @@ -22,3 +24,14 @@ pub use response::Response; pub use status::StatusCode; pub use version::Version; pub use uri::Uri; + +fn _assert_types() { + fn assert_send() {} + fn assert_sync() {} + + assert_send::>(); + assert_send::>(); + + assert_sync::>(); + assert_sync::>(); +} diff --git a/src/request.rs b/src/request.rs index cba59c5..2a86776 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,9 +1,7 @@ //! HTTP request types. -use Uri; +use {Extensions, Method, Uri, Version}; use header::{HeaderMap, HeaderValue}; -use method::Method; -use version::Version; /// Represents an HTTP request. /// @@ -11,7 +9,7 @@ use version::Version; /// component is generic, enabling arbitrary types to represent the HTTP body. /// For example, the body could be `Vec`, a `Stream` of byte chunks, or a /// value that has been deserialized. -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default)] pub struct Request { head: Head, body: T, @@ -21,7 +19,7 @@ pub struct Request { /// /// The HTTP request head consists of a method, uri, version, and a set of /// header fields. -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default)] pub struct Head { /// The request's method pub method: Method, @@ -35,6 +33,9 @@ pub struct Head { /// The request's headers pub headers: HeaderMap, + /// The request's extensions + pub extensions: Extensions, + _priv: (), } @@ -167,6 +168,35 @@ impl Request { &mut self.head.headers } + + /// Returns a reference to the associated extensions. + /// + /// # Examples + /// + /// ``` + /// # use http::*; + /// let request: Request<()> = Request::default(); + /// assert!(request.extensions().get::().is_none()); + /// ``` + pub fn extensions(&self) -> &Extensions { + &self.head.extensions + } + + /// Returns a mutable reference to the associated extensions. + /// + /// # Examples + /// + /// ``` + /// # use http::*; + /// # use http::header::*; + /// let mut request: Request<()> = Request::default(); + /// request.extensions_mut().insert("hello"); + /// assert_eq!(request.extensions().get(), Some(&"hello")); + /// ``` + pub fn extensions_mut(&mut self) -> &mut Extensions { + &mut self.head.extensions + } + /// Returns a reference to the associated HTTP body. /// /// # Examples diff --git a/src/response.rs b/src/response.rs index d674392..c8bcaa6 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,8 +1,7 @@ //! HTTP response types. +use {Extensions, StatusCode, Version}; use header::{HeaderMap, HeaderValue}; -use status::StatusCode; -use version::Version; /// Represents an HTTP response /// @@ -10,7 +9,7 @@ use version::Version; /// component is generic, enabling arbitrary types to represent the HTTP body. /// For example, the body could be `Vec`, a `Stream` of byte chunks, or a /// value that has been deserialized. -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default)] pub struct Response { head: Head, body: T, @@ -20,7 +19,7 @@ pub struct Response { /// /// The HTTP response head consists of a status, version, and a set of /// header fields. -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default)] pub struct Head { /// The response's status pub status: StatusCode, @@ -31,6 +30,9 @@ pub struct Head { /// The response's headers pub headers: HeaderMap, + /// The response's extensions + pub extensions: Extensions, + _priv: (), } @@ -136,6 +138,34 @@ impl Response { &mut self.head.headers } + /// Returns a reference to the associated extensions. + /// + /// # Examples + /// + /// ``` + /// # use http::*; + /// let response: Response<()> = Response::default(); + /// assert!(response.extensions().get::().is_none()); + /// ``` + pub fn extensions(&self) -> &Extensions { + &self.head.extensions + } + + /// Returns a mutable reference to the associated extensions. + /// + /// # Examples + /// + /// ``` + /// # use http::*; + /// # use http::header::*; + /// let mut response: Response<()> = Response::default(); + /// response.extensions_mut().insert("hello"); + /// assert_eq!(response.extensions().get(), Some(&"hello")); + /// ``` + pub fn extensions_mut(&mut self) -> &mut Extensions { + &mut self.head.extensions + } + /// Returns a reference to the associated HTTP body. /// /// # Examples