1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Better scoped thread local storage.

#[doc(hidden)]
pub extern crate scoped_tls;

/// See [scoped_tls::scoped_thread_local] for actual documentation.
///
/// This is identical as [scoped_tls::scoped_thread_local] on release builds.
#[macro_export]
macro_rules! scoped_tls {
    ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => {
        $crate::scoped_tls::scoped_thread_local!(
            static INNER: $ty
        );


        $(#[$attrs])*
        $vis static $name: $crate::ScopedKey<$ty> = $crate::ScopedKey {
            inner: &INNER,
            #[cfg(debug_assertions)]
            module_path: module_path!(),
            #[cfg(debug_assertions)]
            name: stringify!($name),
        };
    };
}

/// Wrapper for [scoped_tls::ScopedKey] with better error messages.
pub struct ScopedKey<T>
where
    T: 'static,
{
    #[doc(hidden)]
    pub inner: &'static scoped_tls::ScopedKey<T>,

    #[cfg(debug_assertions)]
    #[doc(hidden)]
    pub module_path: &'static str,

    #[cfg(debug_assertions)]
    #[doc(hidden)]
    pub name: &'static str,
}

impl<T> ScopedKey<T>
where
    T: 'static,
{
    /// See [scoped_tls::ScopedKey] for actual documentation.
    #[cfg_attr(not(debug_assertions), inline(always))]
    pub fn set<F, R>(&'static self, t: &T, f: F) -> R
    where
        F: FnOnce() -> R,
    {
        self.inner.set(t, f)
    }

    /// See [scoped_tls::ScopedKey] for actual documentation.
    #[track_caller]
    #[cfg_attr(not(debug_assertions), inline(always))]
    pub fn with<F, R>(&'static self, f: F) -> R
    where
        F: FnOnce(&T) -> R,
    {
        #[cfg(debug_assertions)]
        if !self.inner.is_set() {
            // Override panic message
            panic!(
                "You should perform this operation in the closure passed to `set` of {}::{}",
                self.module_path, self.name
            )
        }

        self.inner.with(f)
    }

    /// See [scoped_tls::ScopedKey] for actual documentation.
    #[cfg_attr(not(debug_assertions), inline(always))]
    pub fn is_set(&'static self) -> bool {
        self.inner.is_set()
    }
}

#[cfg(test)]
mod tests {

    scoped_tls!(
        pub static TESTTLS: String
    );

    #[cfg(debug_assertions)]
    #[test]
    #[should_panic = "You should perform this operation in the closure passed to `set` of \
                      better_scoped_tls::tests::TESTTLS"]

    fn panic_on_with() {
        TESTTLS.with(|s| {
            println!("S: {}", s);
        })
    }
    #[test]

    fn valid() {
        TESTTLS.set(&String::new(), || {
            TESTTLS.with(|s| {
                assert_eq!(*s, String::new());
            })
        })
    }
}