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
111
112
113
114
115
116
117
118
119
120
use std::{cell::RefCell, env, path::PathBuf};
pub(crate) const UNKNOWN_FUNCTION: &str = "<unknown>";
thread_local!(pub(crate) static CURRENT: RefCell<Context> = RefCell::new(
Context{
invocation_id: String::new(),
function_id: String::new(),
function_name: UNKNOWN_FUNCTION
}
));
#[derive(Debug, Clone)]
pub struct Context {
pub(crate) invocation_id: String,
pub(crate) function_id: String,
pub(crate) function_name: &'static str,
}
pub(crate) struct ContextGuard;
impl Drop for ContextGuard {
fn drop(&mut self) {
Context::clear();
}
}
impl Context {
pub fn current() -> Option<Self> {
let mut current = None;
CURRENT.with(|c| {
let c = c.borrow();
if !c.invocation_id.is_empty() {
current = Some(c.clone());
}
});
current
}
#[must_use]
pub(crate) fn set(
invocation_id: &str,
function_id: &str,
function_name: &'static str,
) -> ContextGuard {
CURRENT.with(|c| {
let mut c = c.borrow_mut();
c.invocation_id.replace_range(.., invocation_id);
c.function_id.replace_range(.., function_id);
c.function_name = function_name;
});
ContextGuard {}
}
pub(crate) fn clear() {
CURRENT.with(|c| {
let mut c = c.borrow_mut();
c.invocation_id.clear();
c.function_id.clear();
c.function_name = UNKNOWN_FUNCTION;
});
}
pub fn invocation_id(&self) -> &str {
&self.invocation_id
}
pub fn function_id(&self) -> &str {
&self.function_id
}
pub fn function_name(&self) -> &str {
self.function_name
}
pub fn function_directory(&self) -> Option<PathBuf> {
self.app_directory().map(|p| p.join(self.function_name))
}
pub fn app_directory(&self) -> Option<PathBuf> {
env::current_exe()
.map(|p| p.parent().map(ToOwned::to_owned))
.ok()
.unwrap_or(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_returns_none_without_context() {
assert_eq!(Context::current().is_none(), true);
}
#[test]
fn it_returns_current_context() {
let _guard = Context::set("1234", "5678", "foo");
let context = Context::current().unwrap();
assert_eq!(context.invocation_id(), "1234");
assert_eq!(context.function_id(), "5678");
assert_eq!(context.function_name(), "foo");
}
}