use parking_lot::RwLock;
use std::{fmt::Debug, sync::Arc};
pub struct TakeCell<T> {
value: Arc<RwLock<Option<T>>>,
}
impl<T> TakeCell<T> {
pub(crate) fn new(value: T) -> Self {
Self { value: Arc::new(RwLock::new(Some(value))) }
}
pub(crate) fn take_if_raw(&self, predicate: impl FnOnce(&T) -> bool) -> Option<T> {
if self.value.read().as_ref().map_or(false, |v| predicate(v)) {
self.take()
} else {
None
}
}
pub fn take(&self) -> Option<T> {
self.value.write().take()
}
pub fn is_taken(&self) -> bool {
self.value.read().is_none()
}
}
impl<T> Debug for TakeCell<T>
where
T: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let inner = self.value.read();
f.debug_struct("TakeCell")
.field(
"value",
&inner.as_ref().map(|v| v as &dyn Debug).unwrap_or(&"<taken>" as &dyn Debug),
)
.finish()
}
}
impl<T> Clone for TakeCell<T> {
fn clone(&self) -> Self {
Self { value: self.value.clone() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_take() {
let cell = TakeCell::new(1);
assert_eq!(cell.is_taken(), false);
assert_eq!(cell.take(), Some(1));
assert_eq!(cell.take(), None);
assert_eq!(cell.is_taken(), true);
}
#[test]
fn test_take_if_raw() {
let cell = TakeCell::new(1);
assert_eq!(cell.take_if_raw(|value| *value == 2), None);
assert_eq!(cell.take_if_raw(|value| *value == 1), Some(1));
assert_eq!(cell.take_if_raw(|value| *value == 1), None);
}
#[test]
fn test_debug() {
let cell = TakeCell::new(1);
assert_eq!(format!("{:?}", cell), "TakeCell { value: 1 }");
cell.take();
assert_eq!(format!("{:?}", cell), "TakeCell { value: \"<taken>\" }");
}
}