From 8e1c6df7b8d3cf2dcc406f0c73036a5b51b0f92a Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Tue, 30 Aug 2022 19:33:04 +0100 Subject: [PATCH] add some new methods to extensions (#4) * add some new methods to extensions * docs --- README.md | 4 +--- src/extensions.rs | 43 +++++++++++++++++++++++++++++-------------- src/lib.rs | 31 +++++++++++++++++++++++++++++++ src/task_local.rs | 10 +++++++--- 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b8d1d7c..f6cc899 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,7 @@ async fn my_task() { } let a: i64 = 3; -let mut ext = Extensions::new(); -ext.insert(a); -let (out_ext, _) = with_extensions(ext, my_task()).await; +let (out_ext, _) = with_extensions(Extensions::new().with(a), my_task()).await; let msg = out_ext.get::().unwrap(); assert_eq!(msg.as_str(), "The value of a is: 3"); ``` diff --git a/src/extensions.rs b/src/extensions.rs index 6a084c9..8ca0b8c 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -15,13 +15,35 @@ use std::hash::{BuildHasherDefault, Hasher}; /// on the outgoing path (e.g. error class). #[derive(Default)] pub struct Extensions { - map: Option, BuildHasherDefault>>, + map: HashMap, BuildHasherDefault>, } impl Extensions { /// Create an empty `Extensions`. pub fn new() -> Self { - Self { map: None } + Self { + map: HashMap::default(), + } + } + + /// Insert a value ino this [`Extensions`], returning self instead of any pre-inserted values. + /// + /// This is useful for any builder style patterns + /// + /// ``` + /// # use task_local_extensions::Extensions; + /// let ext = Extensions::new().with(true).with(5_i32); + /// assert_eq!(ext.get(), Some(&true)); + /// assert_eq!(ext.get(), Some(&5_i32)); + /// ``` + pub fn with(mut self, val: T) -> Self { + self.insert(val); + self + } + + /// Removes the values from `other` and inserts them into `self`. + pub fn append(&mut self, other: &mut Self) { + self.map.extend(other.map.drain()) } /// Insert a value into this `Extensions`. @@ -29,32 +51,26 @@ impl Extensions { /// If a value of this type already exists, it will be returned. pub fn insert(&mut self, val: T) -> Option { self.map - .get_or_insert_with(Default::default) .insert(TypeId::of::(), Box::new(val)) .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) } /// Check if container contains value for type pub fn contains(&self) -> bool { - self.map - .as_ref() - .and_then(|m| m.get(&TypeId::of::())) - .is_some() + self.map.get(&TypeId::of::()).is_some() } /// Get a reference to a value previously inserted on this `Extensions`. pub fn get(&self) -> Option<&T> { self.map - .as_ref() - .and_then(|m| m.get(&TypeId::of::())) + .get(&TypeId::of::()) .and_then(|boxed| (&**boxed as &(dyn Any)).downcast_ref()) } /// Get a mutable reference to a value previously inserted on this `Extensions`. pub fn get_mut(&mut self) -> Option<&mut T> { self.map - .as_mut() - .and_then(|m| m.get_mut(&TypeId::of::())) + .get_mut(&TypeId::of::()) .and_then(|boxed| (&mut **boxed as &mut (dyn Any)).downcast_mut()) } @@ -63,15 +79,14 @@ impl Extensions { /// If a value of this type exists, it will be returned. pub fn remove(&mut self) -> Option { self.map - .as_mut() - .and_then(|m| m.remove(&TypeId::of::())) + .remove(&TypeId::of::()) .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) } /// Clear the `Extensions` of all inserted values. #[inline] pub fn clear(&mut self) { - self.map = None; + self.map.clear(); } } diff --git a/src/lib.rs b/src/lib.rs index cdefaa3..8ca0335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,35 @@ //! A type map for storing data of arbritrary type. +//! +//! # Extensions +//! [`Extensions`] is a container that can store up to one value of each type, so you can insert and retrive values by +//! their type: +//! +//! ``` +//! use task_local_extensions::Extensions; +//! +//! let a: i64 = 3; +//! let mut ext = Extensions::new(); +//! extensions.insert(a); +//! assert_eq!(ext.get::(), Some(&3)); +//! ``` +//! +//! # Task Local Extensions +//! The crate also provides [`with_extensions`] so you set an [`Extensions`] instance while running a given task: +//! +//! ``` +//! use task_local_extensions::{get_local_item, set_local_item, with_extensions, Extensions}; +//! +//! async fn my_task() { +//! let a: i64 = get_local_item().await.unwrap(0); +//! let msg = format!("The value of a is: {}", a); +//! set_local_item(msg).await; +//! } +//! +//! let a: i64 = 3; +//! let (out_ext, _) = with_extensions(Extensions::new().with(a), my_task()).await; +//! let msg = out_ext.get::().unwrap(); +//! assert_eq!(msg.as_str(), "The value of a is: 3"); +//! ``` mod extensions; mod task_local; diff --git a/src/task_local.rs b/src/task_local.rs index ccac40a..bd26b81 100644 --- a/src/task_local.rs +++ b/src/task_local.rs @@ -1,3 +1,7 @@ +// clippy bug wrongly flags the task_local macro as being bad. +// a fix is already merged but hasn't made it upstream yet +#![allow(clippy::declare_interior_mutable_const)] + use crate::Extensions; use std::cell::RefCell; use std::future::Future; @@ -16,11 +20,11 @@ pub async fn with_extensions( EXTENSIONS .scope(RefCell::new(extensions), async move { let response = fut.await; - let extensions = RefCell::new(Extensions::new()); + let mut extensions = Extensions::new(); - EXTENSIONS.with(|ext| ext.swap(&extensions)); + EXTENSIONS.with(|ext| std::mem::swap(&mut *ext.borrow_mut(), &mut extensions)); - (extensions.into_inner(), response) + (extensions, response) }) .await }