use std::cell::{Cell, RefCell};
use std::ops::Deref;
use std::rc::Rc;
use std::sync::{Arc, Mutex, RwLock, TryLockError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Poisoning<S> {
Healthy(S),
Poisoned(S),
}
impl<S> Poisoning<S> {
#[track_caller]
pub fn assert_healthy(self) -> S {
match self {
Self::Healthy(value) => value,
Self::Poisoned(..) => panic!("Shared instance is poisoned"),
}
}
#[track_caller]
pub fn assert_poisoned(self) -> S {
match self {
Self::Poisoned(value) => value,
Self::Healthy(..) => panic!("Shared instance is not poisoned"),
}
}
pub fn unpoison(self) -> S {
match self {
Self::Healthy(value) => value,
Self::Poisoned(value) => value,
}
}
pub const fn is_healthy(&self) -> bool {
matches!(self, Self::Healthy(..))
}
pub const fn is_poisoned(&self) -> bool {
matches!(self, Self::Poisoned(..))
}
pub const fn as_healthy(&self) -> Option<&S> {
match self {
Self::Healthy(v) => Some(v),
Self::Poisoned(..) => None
}
}
pub const fn as_poisoned(&self) -> Option<&S> {
match self {
Self::Poisoned(v) => Some(v),
Self::Healthy(..) => None
}
}
pub fn as_healthy_mut(&mut self) -> Option<&mut S> {
match self {
Self::Healthy(v) => Some(v),
Self::Poisoned(..) => None
}
}
pub fn as_poisoned_mut(&mut self) -> Option<&mut S> {
match self {
Self::Poisoned(v) => Some(v),
Self::Healthy(..) => None
}
}
pub fn into_healthy(self) -> Option<S> {
match self {
Self::Healthy(v) => Some(v),
Self::Poisoned(..) => None
}
}
pub fn into_poisoned(self) -> Option<S> {
match self {
Self::Poisoned(v) => Some(v),
Self::Healthy(..) => None
}
}
}
pub trait IAccess {
type Target: ?Sized;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U>;
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U;
}
pub trait IAccessMut: IAccess {
fn try_access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> Option<U>;
fn access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> U;
}
#[repr(transparent)]
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Access<T: ?Sized>(T);
impl<T> Access<T> {
pub const fn new(inner: T) -> Self {
Self(inner)
}
pub fn into_inner(self) -> T {
self.0
}
pub const fn inner(&self) -> &T {
&self.0
}
}
impl<T> Deref for Access<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> IAccess for Access<T> {
type Target = T;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U> {
Some(f(Poisoning::Healthy(self.inner())))
}
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U {
f(Poisoning::Healthy(self.inner()))
}
}
impl<T: ?Sized> IAccess for RefCell<T> {
type Target = T;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U> {
match self.try_borrow() {
Ok(bor) => Some(f(Poisoning::Healthy(&bor))),
Err(..) => None,
}
}
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U {
f(Poisoning::Healthy(&self.borrow()))
}
}
impl<T: ?Sized + Copy> IAccess for Cell<T> {
type Target = T;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U> {
Some(self.access(f))
}
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U {
f(Poisoning::Healthy(&self.get()))
}
}
impl<T: ?Sized> IAccess for Mutex<T> {
type Target = T;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U> {
match self.try_lock() {
Ok(lock) => Some(f(Poisoning::Healthy(&lock))),
Err(TryLockError::Poisoned(lock)) => Some(f(Poisoning::Poisoned(&lock.into_inner()))),
Err(..) => None,
}
}
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U {
match self.lock() {
Ok(lock) => f(Poisoning::Healthy(&lock)),
Err(poison) => f(Poisoning::Poisoned(&poison.into_inner())),
}
}
}
impl<T: ?Sized> IAccess for RwLock<T> {
type Target = T;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U> {
match self.try_read() {
Ok(read) => Some(f(Poisoning::Healthy(&read))),
Err(TryLockError::Poisoned(lock)) => Some(f(Poisoning::Poisoned(&lock.into_inner()))),
Err(..) => None,
}
}
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U {
match self.read() {
Ok(read) => f(Poisoning::Healthy(&read)),
Err(poison) => f(Poisoning::Poisoned(&poison.into_inner())),
}
}
}
impl<T: ?Sized + IAccess> IAccess for Rc<T> {
type Target = T::Target;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U> {
self.deref().try_access(f)
}
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U {
self.deref().access(f)
}
}
impl<T: ?Sized + IAccess> IAccess for Arc<T> {
type Target = T::Target;
fn try_access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> Option<U> {
self.deref().try_access(f)
}
fn access<U, F: FnOnce(Poisoning<&Self::Target>) -> U>(&self, f: F) -> U {
self.deref().access(f)
}
}
impl<T: ?Sized> IAccessMut for RefCell<T> {
fn try_access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> Option<U> {
match self.try_borrow_mut() {
Ok(mut bor) => Some(f(Poisoning::Healthy(&mut bor))),
Err(..) => None,
}
}
fn access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> U {
f(Poisoning::Healthy(&mut self.borrow_mut()))
}
}
impl<T: ?Sized + Copy> IAccessMut for Cell<T> {
fn try_access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> Option<U> {
let mut value = self.get();
let output = f(Poisoning::Healthy(&mut value));
self.set(value);
Some(output)
}
fn access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> U {
let mut value = self.get();
let output = f(Poisoning::Healthy(&mut value));
self.set(value);
output
}
}
impl<T: ?Sized> IAccessMut for Mutex<T> {
fn try_access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> Option<U> {
match self.try_lock() {
Ok(mut lock) => Some(f(Poisoning::Healthy(&mut lock))),
Err(TryLockError::Poisoned(lock)) => {
Some(f(Poisoning::Poisoned(&mut lock.into_inner())))
}
Err(..) => None,
}
}
fn access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> U {
match self.lock() {
Ok(mut lock) => f(Poisoning::Healthy(&mut lock)),
Err(poison) => f(Poisoning::Poisoned(&mut poison.into_inner())),
}
}
}
impl<T: ?Sized> IAccessMut for RwLock<T> {
fn try_access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> Option<U> {
match self.try_write() {
Ok(mut write) => Some(f(Poisoning::Healthy(&mut write))),
Err(TryLockError::Poisoned(poison)) => {
Some(f(Poisoning::Poisoned(&mut poison.into_inner())))
}
Err(..) => None,
}
}
fn access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> U {
match self.write() {
Ok(mut write) => f(Poisoning::Healthy(&mut write)),
Err(poison) => f(Poisoning::Poisoned(&mut poison.into_inner())),
}
}
}
impl<T: ?Sized + IAccessMut> IAccessMut for Rc<T> {
fn try_access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> Option<U> {
self.deref().try_access_mut(f)
}
fn access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> U {
self.deref().access_mut(f)
}
}
impl<T: ?Sized + IAccessMut> IAccessMut for Arc<T> {
fn try_access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> Option<U> {
self.deref().try_access_mut(f)
}
fn access_mut<U, F: FnOnce(Poisoning<&mut Self::Target>) -> U>(&self, f: F) -> U {
self.deref().access_mut(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn poisoning_assert_healthy() {
let poison = Poisoning::Healthy(321);
let value = poison.assert_healthy();
assert_eq!(value, 321);
}
#[test]
#[should_panic]
fn poisoning_assert_healthy_panic() {
let poison = Poisoning::Poisoned(100);
let _value = poison.assert_healthy();
}
#[test]
fn poisoning_unpoison() {
let poison = Poisoning::Healthy(321);
let value = poison.unpoison();
assert_eq!(value, 321);
let poison = Poisoning::Poisoned(123);
let value = poison.unpoison();
assert_eq!(value, 123);
}
#[test]
fn poisoning_is_poisoned() {
let poison = Poisoning::Healthy(321);
let is_poisoned = poison.is_poisoned();
assert_eq!(is_poisoned, false);
let poison = Poisoning::Poisoned(123);
let is_poisoned = poison.is_poisoned();
assert_eq!(is_poisoned, true);
}
#[test]
fn poisoning_is_healthy() {
let poison = Poisoning::Healthy(321);
let is_poisoned = poison.is_healthy();
assert_eq!(is_poisoned, true);
let poison = Poisoning::Poisoned(123);
let is_poisoned = poison.is_healthy();
assert_eq!(is_poisoned, false);
}
}