use alloc::sync::Arc;
use core::fmt;
use crate::{IntoListener, Listen, Listener, LoadStore, Notifier, Source};
#[cfg(doc)]
use crate::{sync, unsync};
#[cfg_attr(not(feature = "std-sync"), allow(rustdoc::broken_intra_doc_links))]
pub struct Cell<S, L> {
shared: Arc<CellSource<S, L>>,
}
pub struct CellSource<S, L> {
value_mutex: S,
notifier: Notifier<(), L>,
}
impl<S: LoadStore, L: Listener<()>> Cell<S, L> {
#[must_use]
pub fn new(value: S::Value) -> Self {
Self {
shared: Arc::new(CellSource {
value_mutex: S::new(value),
notifier: Notifier::new(),
}),
}
}
#[must_use]
pub fn as_source(&self) -> Arc<CellSource<S, L>> {
self.shared.clone()
}
#[must_use]
pub fn get(&self) -> S::Value {
self.shared.value_mutex.get()
}
pub fn set(&self, value: S::Value) {
let shared: &CellSource<S, L> = &self.shared;
let old_value = shared.value_mutex.replace(value);
shared.notifier.notify(&());
drop(old_value);
}
pub fn set_if_unequal(&self, value: S::Value)
where
S::Value: PartialEq,
{
match self.shared.value_mutex.replace_if_unequal(value) {
Ok(old_value) => {
self.shared.notifier.notify(&());
drop(old_value);
}
Err(new_value) => drop(new_value),
}
}
pub fn update_mut<F: FnOnce(&mut S::Value)>(&self, f: F) {
let mut value = self.get();
f(&mut value);
self.set(value);
}
}
impl<S, L> Drop for Cell<S, L> {
fn drop(&mut self) {
self.shared.notifier.close()
}
}
impl<S, L: Listener<()>> Listen for CellSource<S, L> {
type Msg = ();
type Listener = L;
fn listen_raw(&self, listener: Self::Listener) {
self.notifier.listen_raw(listener);
}
fn listen<L2: IntoListener<Self::Listener, Self::Msg>>(&self, listener: L2) {
self.notifier.listen(listener)
}
}
impl<S, L: Listener<()>> Listen for Cell<S, L> {
type Msg = ();
type Listener = L;
fn listen_raw(&self, listener: Self::Listener) {
self.shared.listen_raw(listener);
}
fn listen<L2: IntoListener<Self::Listener, Self::Msg>>(&self, listener: L2) {
self.shared.listen(listener)
}
}
impl<S: LoadStore<Value: Clone>, L: Listener<()>> Listen for CellWithLocal<S, L> {
type Msg = ();
type Listener = L;
fn listen_raw(&self, listener: Self::Listener) {
self.cell.listen_raw(listener);
}
fn listen<L2: IntoListener<Self::Listener, Self::Msg>>(&self, listener: L2) {
self.cell.listen(listener)
}
}
impl<S, L> Source for CellSource<S, L>
where
S: LoadStore<Value: fmt::Debug>,
L: Listener<()>,
{
type Value = S::Value;
fn get(&self) -> S::Value {
self.value_mutex.get()
}
}
#[cfg_attr(not(feature = "std-sync"), allow(rustdoc::broken_intra_doc_links))]
pub struct CellWithLocal<S: LoadStore, L> {
cell: Cell<S, L>,
value: S::Value,
}
impl<S, L> CellWithLocal<S, L>
where
S: LoadStore<Value: Clone>,
L: Listener<()>,
{
pub fn new(value: S::Value) -> Self {
Self {
value: value.clone(),
cell: Cell::new(value),
}
}
pub fn as_source(&self) -> Arc<CellSource<S, L>> {
self.cell.as_source()
}
pub fn set(&mut self, value: S::Value) {
self.value = value;
self.cell.set(self.value.clone());
}
pub fn get(&self) -> &S::Value {
&self.value
}
}
impl<S: LoadStore<Value: Default>, L: Listener<()>> Default for Cell<S, L> {
fn default() -> Self {
Self::new(Default::default())
}
}
impl<S: LoadStore<Value: Clone + Default>, L: Listener<()>> Default for CellWithLocal<S, L> {
fn default() -> Self {
Self::new(Default::default())
}
}
impl<S: LoadStore<Value: fmt::Debug>, L: Listener<()>> fmt::Debug for Cell<S, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ds = f.debug_struct("Cell");
ds.field("value", &self.get());
format_cell_metadata(&mut ds, &self.shared);
ds.finish()
}
}
impl<S: LoadStore<Value: fmt::Debug>, L: Listener<()>> fmt::Debug for CellSource<S, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
value_mutex: _, notifier,
} = self;
let mut ds = f.debug_struct("CellSource");
ds.field("value", &self.get());
ds.field("listeners", ¬ifier.count());
ds.finish()
}
}
impl<S: LoadStore<Value: fmt::Debug>, L: Listener<()>> fmt::Debug for CellWithLocal<S, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ds = f.debug_struct("CellWithLocal");
ds.field("value", &self.value);
format_cell_metadata(&mut ds, &self.cell.shared);
ds.finish()
}
}
impl<S, L: Listener<()>> fmt::Pointer for Cell<S, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ds = f.debug_struct("Cell");
ds.field("cell_address", &Arc::as_ptr(&self.shared));
ds.finish()
}
}
impl<S: LoadStore, L: Listener<()>> fmt::Pointer for CellWithLocal<S, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ds = f.debug_struct("CellWithLocal");
ds.field("cell_address", &Arc::as_ptr(&self.cell.shared));
ds.finish()
}
}
fn format_cell_metadata<S, L: Listener<()>>(
ds: &mut fmt::DebugStruct<'_, '_>,
storage: &Arc<CellSource<S, L>>,
) {
ds.field("owners", &Arc::strong_count(storage));
ds.field("listeners", &storage.notifier.count());
}