use std::cell::RefCell;
use lunatic::{process::ProcessRef, Process, ProcessLocal};
use serde::{Deserialize, Serialize};
pub type ProcessCached<T> = CachedProcess<Process<T>>;
pub type ProcessRefCached<T> = CachedProcess<ProcessRef<T>>;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CachedProcess<T> {
lookup_state: RefCell<LookupState<T>>,
process_name: &'static str,
}
impl<T> CachedProcess<T> {
pub fn new(name: &'static str) -> Self {
CachedProcess {
lookup_state: RefCell::new(LookupState::NotLookedUp),
process_name: name,
}
}
pub fn set(&self, value: T) {
*self.lookup_state.borrow_mut() = LookupState::Present(value);
}
pub fn reset(&self) {
*self.lookup_state.borrow_mut() = LookupState::NotLookedUp;
}
}
pub trait CachedLookup<T> {
fn get(self) -> Option<T>;
fn set(self, value: T);
fn reset(self);
}
macro_rules! impl_cached_lookup {
($ty:ident, $cache_ty:ident) => {
impl<T> CachedLookup<$ty<T>> for &'static ProcessLocal<$cache_ty<T>> {
fn get(self) -> Option<$ty<T>> {
lookup(self, |name| $ty::lookup(name))
}
fn set(self, value: $ty<T>) {
set(self, value);
}
fn reset(self) {
reset(self)
}
}
};
}
impl_cached_lookup!(Process, ProcessCached);
impl_cached_lookup!(ProcessRef, ProcessRefCached);
#[macro_export]
macro_rules! cached_process {
(
static $ident:ident : $ty:ty = $name:tt ;
) => {
lunatic::process_local! {
static $ident: $ty = $crate::CachedProcess::new($name);
}
};
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
enum LookupState<T> {
NotLookedUp,
NotPresent,
Present(T),
}
impl<T> Default for LookupState<T> {
fn default() -> Self {
LookupState::NotLookedUp
}
}
fn lookup<F, T>(cached_process: &'static ProcessLocal<CachedProcess<T>>, f: F) -> Option<T>
where
F: Fn(&'static str) -> Option<T>,
T: Clone,
{
cached_process.with(|proc| {
let proc_ref = proc.lookup_state.borrow();
match &*proc_ref {
LookupState::NotLookedUp => {
std::mem::drop(proc_ref);
match f(proc.process_name) {
Some(process) => {
*proc.lookup_state.borrow_mut() = LookupState::Present(process.clone()); Some(process)
}
None => {
*proc.lookup_state.borrow_mut() = LookupState::NotPresent;
None
}
}
}
LookupState::NotPresent => None,
LookupState::Present(process) => {
Some(process.clone()) }
}
})
}
fn set<T>(cached_process: &'static ProcessLocal<CachedProcess<T>>, value: T) {
cached_process.with(|proc| proc.set(value))
}
fn reset<T>(cached_process: &'static ProcessLocal<CachedProcess<T>>) {
cached_process.with(|proc| proc.reset())
}