#!/usr/bin/env -S rust-script -c --debug
use ::devela::{
Any, Deref, DerefMut, Hash, Mem, PhantomData, PtrNonNull, RefCell, any_type_name,
define_static_map, transmute,
};
define_static_map![typeid KeyCurrentMap];
thread_local! {
static CURRENT_PTR_MAP: RefCell<KeyCurrentMap<u64, PtrNonNull<u8>, 64>>
= RefCell::new(KeyCurrentMap::new_cloned(PtrNonNull::<u8>::dangling()));
}
#[doc = crate::_tags!(guard)]
#[doc = crate::_doc_location!("sys/mem")]
#[cfg_attr(doc, doc = ::devela::_doc!(vendor: "current"))]
#[derive(Debug)]
pub struct CurrentGuard<'a, T: Any> {
_current: &'a mut T,
prev_ptr: Option<PtrNonNull<T>>,
}
impl<T: Any> CurrentGuard<'_, T> {
pub fn new(current: &mut T) -> CurrentGuard<'_, T> {
let ptr = PtrNonNull::from(&mut *current).cast::<u8>();
let prev_ptr = CURRENT_PTR_MAP.with(|current| {
let mut map = current.borrow_mut();
if let Some(entry) = map.get_mut_type::<T>() {
Some(Mem::replace(entry, ptr).cast::<T>())
} else {
map.insert_type::<T>(ptr.cast()).ok();
None
}
});
CurrentGuard { prev_ptr, _current: current }
}
}
impl<T: Any> Drop for CurrentGuard<'_, T> {
fn drop(&mut self) {
CURRENT_PTR_MAP.with(|current| {
let mut map = current.borrow_mut();
match self.prev_ptr {
None => map.insert_type::<T>(PtrNonNull::<u8>::dangling().cast()).ok(),
Some(prev_ptr) => map.insert_type::<T>(prev_ptr.cast()).ok(),
}
});
}
}
#[doc = crate::_tags!(guard)]
#[doc = crate::_doc_location!("sys/mem")]
#[cfg_attr(doc, doc = ::devela::_doc!(vendor: "current"))]
#[derive(Debug)]
pub struct Current<T>(PhantomData<T>);
impl<T: Any> Current<T> {
pub unsafe fn new() -> Current<T> {
Current(PhantomData)
}
#[rustfmt::skip]
pub unsafe fn current(&mut self) -> Option<&mut T> {
let ptr: PtrNonNull<u8> = CURRENT_PTR_MAP.with(|current| current.borrow().get_type::<T>())?;
Some(unsafe { &mut *ptr.cast::<T>().as_ptr() })
}
pub unsafe fn current_unwrap(&mut self) -> &mut T {
match unsafe { self.current() } {
None => panic!("No current `{}` is set", any_type_name::<T>()),
Some(x) => x,
}
}
}
impl<T: Any> Deref for Current<T> {
type Target = T;
#[inline(always)] #[rustfmt::skip]
fn deref<'a>(&'a self) -> &'a T {
#[allow(mutable_transmutes, reason = "`Current` only acts as a reference handle")]
unsafe { transmute::<&Current<T>, &'a mut Current<T>>(self).current_unwrap() }
}
}
impl<T: Any> DerefMut for Current<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
unsafe { self.current_unwrap() }
}
}
#[allow(unused, reason = "example script")]
fn main() {
struct State {
text: String,
}
impl State {
fn print() {
let mut ctx = unsafe { Current::<State>::new() };
println!("{}", ctx.text);
ctx.text = "world!".to_string();
}
fn bar() {
let mut bar = State { text: "good bye".to_string() };
let guard = CurrentGuard::new(&mut bar);
State::print(); State::print(); }
}
let mut ctx = State { text: "hello".to_string() };
let guard = CurrentGuard::new(&mut ctx);
State::print(); State::print(); State::bar(); }