use std::any::Any;
use std::cell::{Cell, RefCell};
use crate::registry::ServiceRegistry;
type Stack = *mut RefCell<Vec<ServiceRegistry>>;
#[derive(Clone, Copy)]
struct StackSlot {
live: Stack,
ambient: Stack,
}
thread_local! {
static STACK: Cell<StackSlot> = {
let ambient: Stack = Box::into_raw(Box::new(RefCell::new(vec![ServiceRegistry::new()])));
Cell::new(StackSlot { live: ambient, ambient })
};
}
fn with_stack<R>(f: impl FnOnce(&RefCell<Vec<ServiceRegistry>>) -> R) -> R {
STACK.with(|cell| unsafe { f(&*cell.get().live) })
}
fn swap_live(next: Stack) -> Stack {
STACK.with(|cell| {
let mut slot = cell.get();
let prev = std::mem::replace(&mut slot.live, next);
cell.set(slot);
prev
})
}
pub fn provide<T: Any + 'static>(service: T) -> Result<(), crate::registry::ServiceError> {
with_stack(|stack| {
stack
.borrow_mut()
.last_mut()
.expect("service stack is empty — this is a bug")
.insert(service)
})
}
pub fn try_inject<T: Any + Clone + 'static>() -> Option<T> {
with_stack(|stack| {
stack
.borrow()
.last()
.and_then(|scope| scope.get::<T>())
.cloned()
})
}
pub fn with_service<T: Any + 'static, R>(f: impl FnOnce(&T) -> R) -> Option<R> {
with_stack(|stack| {
let stack = stack.borrow();
stack.last().and_then(|scope| scope.get::<T>()).map(f)
})
}
pub struct Scope(());
impl Scope {
pub fn with<R>(f: impl FnOnce() -> R) -> R {
with_stack(|stack| {
let mut stack = stack.borrow_mut();
let mut new_scope = ServiceRegistry::new();
if let Some(parent) = stack.last() {
new_scope.merge_from(parent);
}
stack.push(new_scope);
});
struct PopGuard;
impl Drop for PopGuard {
fn drop(&mut self) {
with_stack(|stack| {
let mut stack = stack.borrow_mut();
if stack.len() > 1 {
stack.pop();
}
});
}
}
let _guard = PopGuard;
f()
}
}
pub struct ServiceContext {
ptr: *mut RefCell<Vec<ServiceRegistry>>,
}
impl ServiceContext {
pub fn new() -> Self {
Self {
ptr: Box::into_raw(Box::new(RefCell::new(vec![ServiceRegistry::new()]))),
}
}
#[must_use = "the surface's services are only active while this guard is alive"]
pub fn enter(&self) -> ServiceGuard {
ServiceGuard {
prev: swap_live(self.ptr),
}
}
#[must_use = "the ambient services are only active while this guard is alive"]
pub fn enter_ambient() -> ServiceGuard {
let ambient = STACK.with(|cell| cell.get().ambient);
ServiceGuard {
prev: swap_live(ambient),
}
}
}
impl Default for ServiceContext {
fn default() -> Self {
Self::new()
}
}
impl Drop for ServiceContext {
fn drop(&mut self) {
unsafe { drop(Box::from_raw(self.ptr)) };
}
}
#[must_use = "the surface's services are only active while this guard is alive"]
pub struct ServiceGuard {
prev: *mut RefCell<Vec<ServiceRegistry>>,
}
impl Drop for ServiceGuard {
fn drop(&mut self) {
swap_live(self.prev);
}
}