use std::cell::RefCell;
use lunatic::{process::ProcessRef, serializer::Bincode, Process, ProcessLocal};
use serde::{Deserialize, Serialize};
pub type ProcessCached<'a, T, S = Bincode> = CachedProcess<'a, Process<T, S>>;
pub type ProcessRefCached<'a, T> = CachedProcess<'a, ProcessRef<T>>;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CachedProcess<'a, T> {
lookup_state: RefCell<LookupState<T>>,
process_name: &'a str,
}
impl<'a, T> CachedProcess<'a, T> {
pub fn new(name: &'a str) -> Self {
CachedProcess {
lookup_state: RefCell::new(LookupState::NotLookedUp),
process_name: name,
}
}
pub fn process_name(&'a self) -> &'a str {
self.process_name
}
pub fn is_present(&'a self) -> bool {
matches!(&*self.lookup_state.borrow(), LookupState::Present(_))
}
pub fn is_looked_up(&'a self) -> bool {
matches!(&*self.lookup_state.borrow(), LookupState::NotLookedUp)
}
}
pub trait CachedLookup<'a, T> {
fn get(&'a self) -> Option<T>;
fn set(&'a self, value: T);
fn reset(&'a self);
}
impl<T, S> CachedLookup<'static, Process<T, S>> for ProcessLocal<ProcessCached<'_, T, S>> {
#[inline]
fn get(&'static self) -> Option<Process<T, S>> {
self.with(|proc| lookup(proc, |name| Process::lookup(name)))
}
#[inline]
fn set(&'static self, value: Process<T, S>) {
self.with(|proc| CachedLookup::set(proc, value))
}
#[inline]
fn reset(&'static self) {
self.with(CachedLookup::reset)
}
}
impl<T, S> CachedLookup<'static, Process<T, S>> for ProcessCached<'_, T, S> {
#[inline]
fn get(&'static self) -> Option<Process<T, S>> {
lookup(self, |name| Process::lookup(name))
}
#[inline]
fn set(&'static self, value: Process<T, S>) {
*self.lookup_state.borrow_mut() = LookupState::Present(value);
}
#[inline]
fn reset(&'static self) {
*self.lookup_state.borrow_mut() = LookupState::NotLookedUp;
}
}
impl<T> CachedLookup<'static, ProcessRef<T>> for ProcessLocal<ProcessRefCached<'_, T>> {
#[inline]
fn get(&'static self) -> Option<ProcessRef<T>> {
self.with(|proc| lookup(proc, |name| ProcessRef::lookup(name)))
}
#[inline]
fn set(&'static self, value: ProcessRef<T>) {
self.with(|proc| CachedLookup::set(proc, value))
}
#[inline]
fn reset(&'static self) {
self.with(CachedLookup::reset)
}
}
impl<T> CachedLookup<'static, ProcessRef<T>> for ProcessRefCached<'_, T> {
#[inline]
fn get(&'static self) -> Option<ProcessRef<T>> {
lookup(self, |name| ProcessRef::lookup(name))
}
#[inline]
fn set(&'static self, value: ProcessRef<T>) {
*self.lookup_state.borrow_mut() = LookupState::Present(value);
}
#[inline]
fn reset(&'static self) {
*self.lookup_state.borrow_mut() = LookupState::NotLookedUp;
}
}
#[macro_export]
macro_rules! cached_process {
(
$(
static $ident:ident : $process_type:ident <$ty:ty $( , $s:ty )?> = $name:tt ;
)+
) => {
paste::paste! {
$(
lunatic::process_local! {
static $ident: $crate:: [<$process_type Cached>] <'static, $ty $( , $s )?> = $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
}
}
#[inline]
fn lookup<'a, F, T>(proc: &'a CachedProcess<T>, f: F) -> Option<T>
where
F: Fn(&'a str) -> Option<T>,
T: Clone,
{
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()) }
}
}