#![allow(unreachable_pub, dead_code)]
use std::{
ops::{Deref, DerefMut},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};
use tokio::sync::{Mutex, MutexGuard};
#[derive(Debug)]
pub struct Nutex<T: Sized> {
mutex: Mutex<Option<T>>,
is_some: Arc<AtomicBool>,
}
#[derive(Debug)]
#[clippy::has_significant_drop]
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct NutexGuard<'a, T: Sized> {
lock: &'a Nutex<T>,
guard: MutexGuard<'a, Option<T>>,
}
impl<'a, T: Sized> Nutex<T> {
pub fn new() -> Self {
Self::default()
}
pub async fn get(&self) -> T
where
T: Clone + Sized,
{
self.lock().await.clone()
}
pub fn blocking_get(&self) -> T
where
T: Clone + Sized,
{
self.blocking_lock().clone()
}
pub async fn safe_get(&self) -> Option<T>
where
T: Clone + Sized,
{
self.safe_lock().await.map(|g| g.clone())
}
pub fn safe_blocking_get(&self) -> Option<T>
where
T: Clone + Sized,
{
self.safe_blocking_lock().map(|g| g.clone())
}
pub async fn set(&self, val: T) {
if self.is_some.load(Ordering::Relaxed) {
*self.mutex.lock().await = Some(val);
self.is_some.swap(true, Ordering::Relaxed);
}
}
pub fn blocking_set(&self, val: T) {
if self.is_some.load(Ordering::Relaxed) {
*self.mutex.blocking_lock() = Some(val);
self.is_some.swap(true, Ordering::Relaxed);
}
}
pub async fn clear(&self) {
*self.mutex.lock().await = None;
}
pub fn blocking_clear(&self) {
*self.mutex.blocking_lock() = None;
}
fn guard(&'a self, guard: MutexGuard<'a, Option<T>>) -> NutexGuard<'a, T> {
if self.is_some.load(Ordering::Relaxed) {
NutexGuard {
lock: self,
guard: guard,
}
} else {
panic!(
"Accessed inner Nutex value that's `None`.
If you're not sure that value is certainly set,
prefer using `.safe_lock()` or `.safe_blocking_lock()` methods instead."
)
}
}
fn guard_expect(&'a self, guard: MutexGuard<'a, Option<T>>, msg: &str) -> NutexGuard<'a, T> {
if self.is_some.load(Ordering::Relaxed) {
NutexGuard {
lock: self,
guard: guard,
}
} else {
panic!("{}", msg)
}
}
fn safe_guard(&'a self, guard: MutexGuard<'a, Option<T>>) -> Option<NutexGuard<'a, T>> {
if self.is_some.load(Ordering::Relaxed) {
Some(NutexGuard {
lock: self,
guard: guard,
})
} else {
None
}
}
pub async fn lock(&'a self) -> NutexGuard<'a, T> {
self.guard(self.mutex.lock().await)
}
pub async fn lock_expect(&'a self, msg: &str) -> NutexGuard<'a, T> {
self.guard_expect(self.mutex.lock().await, msg)
}
pub fn blocking_lock(&'a self) -> NutexGuard<'a, T> {
self.guard(self.mutex.blocking_lock())
}
pub fn blocking_lock_expect(&'a self, msg: &str) -> NutexGuard<'a, T> {
self.guard_expect(self.mutex.blocking_lock(), msg)
}
pub async fn safe_lock(&'a self) -> Option<NutexGuard<'a, T>> {
self.safe_guard(self.mutex.lock().await)
}
pub fn safe_blocking_lock(&'a self) -> Option<NutexGuard<'a, T>> {
self.safe_guard(self.mutex.blocking_lock())
}
pub fn is_some(&self) -> bool {
self.is_some.load(Ordering::Relaxed)
}
pub fn is_none(&self) -> bool {
!self.is_some.load(Ordering::Relaxed)
}
pub fn into_inner(self) -> Option<T> {
self.mutex.into_inner()
}
}
impl<T: Sized> Default for Nutex<T> {
fn default() -> Self {
Self {
mutex: Mutex::new(None),
is_some: Arc::new(AtomicBool::new(false)),
}
}
}
impl<T: Sized> From<T> for Nutex<T> {
fn from(value: T) -> Self {
Self {
mutex: Mutex::new(Some(value)),
is_some: Arc::new(AtomicBool::new(true)),
}
}
}
impl<'a, T: Sized> NutexGuard<'a, T> {
pub fn nutex(&self) -> &'a Nutex<T> {
self.lock
}
}
impl<'a, T: Sized> Deref for NutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &'a Self::Target {
match &*self.guard {
Some(val) => unsafe { std::mem::transmute::<&T, &'a T>(val) },
None => unreachable!("MutexGuard can be accessed only if value exist"),
}
}
}
impl<'a, T: Sized> DerefMut for NutexGuard<'a, T> {
fn deref_mut(&mut self) -> &'a mut Self::Target {
match &mut *self.guard {
Some(val) => unsafe { std::mem::transmute::<&mut T, &'a mut T>(val) },
None => unreachable!("MutexGuard can be accessed only if value exist"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn access_test() {
let nutex = Nutex::from(String::from("foo"));
assert_eq!(String::from("foo"), *nutex.blocking_lock())
}
#[test]
fn mutation_test() {
let nutex: Nutex<String> = Nutex::new();
nutex.blocking_set(String::from("bar"));
assert_eq!(String::from("bar"), *nutex.blocking_lock())
}
#[test]
fn clear_test() {
let nutex: Nutex<String> = Nutex::new();
nutex.blocking_set(String::from("bar"));
assert_eq!(String::from("bar"), *nutex.blocking_lock());
nutex.blocking_clear();
assert!(nutex.safe_blocking_lock().is_none());
}
}