1#[doc(hidden)]
4pub extern crate scoped_tls;
5
6#[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
28pub 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 #[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 #[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 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 #[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}