#![warn(clippy::pedantic, clippy::allow_attributes)]
#![doc = include_str!("../README.md")]
#[cfg(test)]
mod tests;
use std::{
fmt::{Debug, Display},
ops::Deref,
ptr::NonNull,
sync::{PoisonError, RwLock, RwLockReadGuard},
};
fn ignore_poison<T>(v: Result<T, PoisonError<T>>) -> T {
v.unwrap_or_else(PoisonError::into_inner)
}
pub struct AssignedError<F> {
pub body: F,
}
impl<F> Debug for AssignedError<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AssignError").finish_non_exhaustive()
}
}
impl<F> Display for AssignedError<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Scope already assigned")
}
}
#[derive(Default, Debug)]
pub struct Scope<T: ?Sized> {
value: RwLock<Option<NonNull<T>>>,
}
unsafe impl<T: Sync + ?Sized> Send for Scope<T> {}
unsafe impl<T: Sync + ?Sized> Sync for Scope<T> {}
#[derive(Debug, Clone)]
pub struct WeakRef<'scope, T: ?Sized> {
source: &'scope Scope<T>,
}
#[derive(Debug)]
pub struct StrongRef<'scope, T: ?Sized> {
source: &'scope Scope<T>,
guard: RwLockReadGuard<'scope, Option<NonNull<T>>>,
}
impl<'scope, T: ?Sized> StrongRef<'scope, T> {
#[must_use]
pub fn downgrade(&self) -> WeakRef<'scope, T> {
self.source.weak_ref()
}
}
impl<T: ?Sized> Clone for StrongRef<'_, T> {
fn clone(&self) -> Self {
self.source.strong_ref().unwrap()
}
}
unsafe impl<T: Sync + ?Sized> Sync for StrongRef<'_, T> {}
impl<T: ?Sized> Scope<T> {
#[must_use]
pub const fn new() -> Self {
Self {
value: RwLock::new(None),
}
}
pub fn assign<R, F: FnOnce() -> R>(&self, target: &T, body: F) -> Result<R, AssignedError<F>> {
{
let mut guard = ignore_poison(self.value.write());
if guard.is_some() {
return Err(AssignedError { body });
}
*guard = Some(NonNull::from_ref(target));
}
let _clear = EmptyScopeOnDrop { target: self };
Ok(body())
}
#[must_use]
pub const fn weak_ref(&self) -> WeakRef<'_, T> {
WeakRef { source: self }
}
#[must_use]
pub fn strong_ref(&self) -> Option<StrongRef<'_, T>> {
self.weak_ref().upgrade()
}
}
impl<'scope, T: ?Sized> WeakRef<'scope, T> {
#[must_use]
pub fn upgrade(&self) -> Option<StrongRef<'scope, T>> {
let guard = ignore_poison(self.source.value.read());
if guard.is_none() {
None
} else {
Some(StrongRef {
source: self.source,
guard,
})
}
}
}
impl<T: ?Sized> Deref for StrongRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.guard.unwrap_unchecked().as_ref() }
}
}
struct EmptyScopeOnDrop<'a, T: ?Sized> {
target: &'a Scope<T>,
}
impl<T: ?Sized> Drop for EmptyScopeOnDrop<'_, T> {
fn drop(&mut self) {
*ignore_poison(self.target.value.write()) = None;
}
}