diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index ed3764587..8b0255286 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -12,6 +12,7 @@ `mime` types. [#1894] * Renamed `IntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894] +* `Extensions::insert` returns Option of replaced item. [#1904] ### Removed * `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869] @@ -22,6 +23,7 @@ [#1869]: https://github.com/actix/actix-web/pull/1869 [#1894]: https://github.com/actix/actix-web/pull/1894 +[#1904]: https://github.com/actix/actix-web/pull/1904 ## 3.0.0-beta.1 - 2021-01-07 diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index e978c1749..5fdcefd6d 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -5,7 +5,9 @@ use std::{ use ahash::AHashMap; -/// A type map of request extensions. +/// A type map for request extensions. +/// +/// All entries into this map must be owned types (or static references). #[derive(Default)] pub struct Extensions { /// Use FxHasher with a std HashMap with for faster @@ -14,7 +16,7 @@ pub struct Extensions { } impl Extensions { - /// Create an empty `Extensions`. + /// Creates an empty `Extensions`. #[inline] pub fn new() -> Extensions { Extensions { @@ -22,43 +24,96 @@ impl Extensions { } } - /// Insert a type into this `Extensions`. + /// Insert an item into the map. /// - /// If a extension of this type already existed, it will - /// be returned. - pub fn insert(&mut self, val: T) { - self.map.insert(TypeId::of::(), Box::new(val)); + /// If an item of this type was already stored, it will be replaced and returned. + /// + /// ``` + /// # use actix_http::Extensions; + /// let mut map = Extensions::new(); + /// assert_eq!(map.insert(""), None); + /// assert_eq!(map.insert(1u32), None); + /// assert_eq!(map.insert(2u32), Some(1u32)); + /// assert_eq!(*map.get::().unwrap(), 2u32); + /// ``` + pub fn insert(&mut self, val: T) -> Option { + self.map + .insert(TypeId::of::(), Box::new(val)) + .and_then(downcast_owned) } - /// Check if container contains entry + /// Check if map contains an item of a given type. + /// + /// ``` + /// # use actix_http::Extensions; + /// let mut map = Extensions::new(); + /// assert!(!map.contains::()); + /// + /// assert_eq!(map.insert(1u32), None); + /// assert!(map.contains::()); + /// ``` pub fn contains(&self) -> bool { self.map.contains_key(&TypeId::of::()) } - /// Get a reference to a type previously inserted on this `Extensions`. + /// Get a reference to an item of a given type. + /// + /// ``` + /// # use actix_http::Extensions; + /// let mut map = Extensions::new(); + /// map.insert(1u32); + /// assert_eq!(map.get::(), Some(&1u32)); + /// ``` pub fn get(&self) -> Option<&T> { self.map .get(&TypeId::of::()) .and_then(|boxed| boxed.downcast_ref()) } - /// Get a mutable reference to a type previously inserted on this `Extensions`. + /// Get a mutable reference to an item of a given type. + /// + /// ``` + /// # use actix_http::Extensions; + /// let mut map = Extensions::new(); + /// map.insert(1u32); + /// assert_eq!(map.get_mut::(), Some(&mut 1u32)); + /// ``` pub fn get_mut(&mut self) -> Option<&mut T> { self.map .get_mut(&TypeId::of::()) .and_then(|boxed| boxed.downcast_mut()) } - /// Remove a type from this `Extensions`. + /// Remove an item from the map of a given type. /// - /// If a extension of this type existed, it will be returned. + /// If an item of this type was already stored, it will be returned. + /// + /// ``` + /// # use actix_http::Extensions; + /// let mut map = Extensions::new(); + /// + /// map.insert(1u32); + /// assert_eq!(map.get::(), Some(&1u32)); + /// + /// assert_eq!(map.remove::(), Some(1u32)); + /// assert!(!map.contains::()); + /// ``` pub fn remove(&mut self) -> Option { - self.map - .remove(&TypeId::of::()) - .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed)) + self.map.remove(&TypeId::of::()).and_then(downcast_owned) } /// Clear the `Extensions` of all inserted extensions. + /// + /// ``` + /// # use actix_http::Extensions; + /// let mut map = Extensions::new(); + /// + /// map.insert(1u32); + /// assert!(map.contains::()); + /// + /// map.clear(); + /// assert!(!map.contains::()); + /// ``` #[inline] pub fn clear(&mut self) { self.map.clear(); @@ -81,6 +136,10 @@ impl fmt::Debug for Extensions { } } +fn downcast_owned(boxed: Box) -> Option { + boxed.downcast().ok().map(|boxed| *boxed) +} + #[cfg(test)] mod tests { use super::*; diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index fe7825d78..f20cfd70c 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -408,7 +408,9 @@ async fn test_h2_service_error() { async fn test_h2_on_connect() { let srv = test_server(move || { HttpService::build() - .on_connect_ext(|_, data| data.insert(20isize)) + .on_connect_ext(|_, data| { + data.insert(20isize); + }) .h2(|req: Request| { assert!(req.extensions().contains::()); ok::<_, ()>(Response::Ok().finish()) diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index e1cfe9db4..b4ef74406 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -662,7 +662,9 @@ async fn test_h1_service_error() { async fn test_h1_on_connect() { let srv = test_server(|| { HttpService::build() - .on_connect_ext(|_, data| data.insert(20isize)) + .on_connect_ext(|_, data| { + data.insert(20isize); + }) .h1(|req: Request| { assert!(req.extensions().contains::()); future::ok::<_, ()>(Response::Ok().finish())