better_scoped_tls/
lib.rs

1//! Better scoped thread local storage.
2
3#[doc(hidden)]
4pub extern crate scoped_tls;
5
6/// See [scoped_tls::scoped_thread_local] for actual documentation.
7///
8/// This is identical as [scoped_tls::scoped_thread_local] on release builds.
9#[macro_export]
10macro_rules! scoped_tls {
11    ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => {
12        $crate::scoped_tls::scoped_thread_local!(
13            static INNER: $ty
14        );
15
16
17        $(#[$attrs])*
18        $vis static $name: $crate::ScopedKey<$ty> = $crate::ScopedKey {
19            inner: &INNER,
20            #[cfg(debug_assertions)]
21            module_path: module_path!(),
22            #[cfg(debug_assertions)]
23            name: stringify!($name),
24        };
25    };
26}
27
28/// Wrapper for [scoped_tls::ScopedKey] with better error messages.
29pub struct ScopedKey<T>
30where
31    T: 'static,
32{
33    #[doc(hidden)]
34    pub inner: &'static scoped_tls::ScopedKey<T>,
35
36    #[cfg(debug_assertions)]
37    #[doc(hidden)]
38    pub module_path: &'static str,
39
40    #[cfg(debug_assertions)]
41    #[doc(hidden)]
42    pub name: &'static str,
43}
44
45impl<T> ScopedKey<T>
46where
47    T: 'static,
48{
49    /// See [scoped_tls::ScopedKey] for actual documentation.
50    #[cfg_attr(not(debug_assertions), inline(always))]
51    pub fn set<F, R>(&'static self, t: &T, f: F) -> R
52    where
53        F: FnOnce() -> R,
54    {
55        self.inner.set(t, f)
56    }
57
58    /// See [scoped_tls::ScopedKey] for actual documentation.
59    #[track_caller]
60    #[cfg_attr(not(debug_assertions), inline(always))]
61    pub fn with<F, R>(&'static self, f: F) -> R
62    where
63        F: FnOnce(&T) -> R,
64    {
65        #[cfg(debug_assertions)]
66        if !self.inner.is_set() {
67            // Override panic message
68            panic!(
69                "You should perform this operation in the closure passed to `set` of {}::{}",
70                self.module_path, self.name
71            )
72        }
73
74        self.inner.with(f)
75    }
76
77    /// See [scoped_tls::ScopedKey] for actual documentation.
78    #[cfg_attr(not(debug_assertions), inline(always))]
79    pub fn is_set(&'static self) -> bool {
80        self.inner.is_set()
81    }
82}
83
84#[cfg(test)]
85mod tests {
86
87    scoped_tls!(
88        pub static TESTTLS: String
89    );
90
91    #[cfg(debug_assertions)]
92    #[test]
93    #[should_panic = "You should perform this operation in the closure passed to `set` of \
94                      better_scoped_tls::tests::TESTTLS"]
95    fn panic_on_with() {
96        TESTTLS.with(|s| {
97            println!("S: {}", s);
98        })
99    }
100    #[test]
101
102    fn valid() {
103        TESTTLS.set(&String::new(), || {
104            TESTTLS.with(|s| {
105                assert_eq!(*s, String::new());
106            })
107        })
108    }
109}