use std::any::Any;
use std::cell::RefCell;
use std::thread::LocalKey;
#[derive(Debug, Default)]
pub struct Workspace {
workspaces: Vec<Box<dyn Any>>,
}
impl Workspace {
pub fn get_or_insert_with<W, F>(&mut self, create: F) -> &mut W
where
W: 'static,
F: FnOnce() -> W,
{
let existing_ws_idx = self.workspaces.iter().rposition(|ws| ws.is::<W>());
let idx = match existing_ws_idx {
Some(idx) => idx,
None => {
let w = create();
let idx = self.workspaces.len();
self.workspaces.push(Box::new(w) as Box<dyn Any>);
idx
}
};
let last = self.workspaces.len() - 1;
self.workspaces.swap(idx, last);
let entry = &mut self.workspaces[last];
entry
.downcast_mut()
.expect("Internal error: Downcasting can by definition not fail")
}
pub fn get_or_default<W>(&mut self) -> &mut W
where
W: 'static + Default,
{
self.get_or_insert_with(Default::default)
}
}
pub fn with_thread_local_workspace<W: 'static + Default, T>(
workspace: &'static LocalKey<RefCell<Workspace>>,
f: impl FnOnce(&mut W) -> T,
) -> T {
workspace.with(|refcell_ws| {
let mut type_erased_workspace = refcell_ws.borrow_mut();
let workspace = type_erased_workspace.get_or_default();
f(workspace)
})
}
#[macro_export]
macro_rules! define_thread_local_workspace {
($variable_name:ident) => {
thread_local! {
static $variable_name: std::cell::RefCell<$crate::workspace::Workspace>
= std::cell::RefCell::new($crate::workspace::Workspace::default());
}
};
}