use std::cell::RefCell;
thread_local! {
static ACTIVE_REQUEST_ID_STACK: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
}
#[must_use = "dropping the guard immediately pops the request_id scope"]
pub struct RequestIdScopeGuard {
_private: (),
}
impl Drop for RequestIdScopeGuard {
fn drop(&mut self) {
ACTIVE_REQUEST_ID_STACK.with(|stack| {
stack.borrow_mut().pop();
});
}
}
pub fn enter_request_id(request_id: impl Into<String>) -> RequestIdScopeGuard {
ACTIVE_REQUEST_ID_STACK.with(|stack| stack.borrow_mut().push(request_id.into()));
RequestIdScopeGuard { _private: () }
}
pub fn current_request_id() -> Option<String> {
ACTIVE_REQUEST_ID_STACK.with(|stack| stack.borrow().last().cloned())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn current_returns_none_when_nothing_pushed() {
assert_eq!(current_request_id(), None);
}
#[test]
fn guard_pops_on_drop_and_inner_scope_shadows_outer() {
let outer = enter_request_id("req_outer");
assert_eq!(current_request_id().as_deref(), Some("req_outer"));
{
let _inner = enter_request_id("req_inner");
assert_eq!(current_request_id().as_deref(), Some("req_inner"));
}
assert_eq!(current_request_id().as_deref(), Some("req_outer"));
drop(outer);
assert_eq!(current_request_id(), None);
}
}