use core::fmt::Debug;
use core::marker::PhantomData;
use core::pin::Pin;
use super::slot::Slot;
use super::{Storable, generational};
#[derive(Debug)]
pub struct Writer<'a, T>
where
T: Storable + 'static,
{
slot: Pin<&'a Slot<T>>,
waiter: generational::Waiter<'a>,
marker: PhantomData<fn(T)>,
}
impl<T> Writer<'_, T>
where
T: Storable + 'static,
{
#[cfg_attr(feature = "veecle-telemetry", veecle_telemetry::instrument)]
pub async fn write(&mut self, item: T::DataType) {
self.modify(|slot| {
let _ = slot.insert(item);
})
.await;
}
pub async fn ready(&mut self) {
let _ = self.waiter.wait().await;
}
#[cfg_attr(feature = "veecle-telemetry", veecle_telemetry::instrument)]
pub async fn modify(&mut self, f: impl FnOnce(&mut Option<T::DataType>)) {
self.ready().await;
self.waiter.update_generation();
#[cfg(feature = "veecle-telemetry")]
let type_name = self.slot.inner_type_name();
self.slot.modify(|value| {
f(value);
#[cfg(feature = "veecle-telemetry")]
veecle_telemetry::trace!("Type update.", type_name);
});
self.slot.increment_generation();
}
#[cfg_attr(feature = "veecle-telemetry", veecle_telemetry::instrument)]
pub fn read<U>(&self, f: impl FnOnce(Option<&T::DataType>) -> U) -> U {
#[cfg(feature = "veecle-telemetry")]
let type_name = self.slot.inner_type_name();
self.slot.read(|value| {
let value = value.as_ref();
#[cfg(feature = "veecle-telemetry")]
veecle_telemetry::trace!("Slot read.", type_name);
f(value)
})
}
}
impl<'a, T> Writer<'a, T>
where
T: Storable + 'static,
{
pub(crate) fn new(waiter: generational::Waiter<'a>, slot: Pin<&'a Slot<T>>) -> Self {
slot.take_writer();
Self {
slot,
waiter,
marker: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use crate::datastore::{Slot, Storable, Writer, generational};
use core::pin::pin;
#[test]
fn ready_waits_for_increment() {
use futures::FutureExt;
#[derive(Debug)]
pub struct Data();
impl Storable for Data {
type DataType = Self;
}
let source = pin!(generational::Source::new());
let slot = pin!(Slot::<Data>::new());
let mut writer = Writer::new(source.as_ref().waiter(), slot.as_ref());
assert!(writer.ready().now_or_never().is_none());
assert!(writer.write(Data {}).now_or_never().is_none());
source.as_ref().increment_generation();
assert!(writer.ready().now_or_never().is_some());
assert!(writer.write(Data {}).now_or_never().is_some());
assert!(writer.ready().now_or_never().is_none());
assert!(writer.write(Data {}).now_or_never().is_none());
}
#[test]
fn read_reads_latest_written_value() {
use futures::FutureExt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Data(usize);
impl Storable for Data {
type DataType = Self;
}
let source = pin!(generational::Source::new());
let slot = pin!(Slot::<Data>::new());
let mut writer = Writer::new(source.as_ref().waiter(), slot.as_ref());
writer.read(|current_data| assert!(current_data.is_none()));
source.as_ref().increment_generation();
let want = Data(1);
writer.write(want).now_or_never().unwrap();
writer.read(|got| assert_eq!(got, Some(&want)));
source.as_ref().increment_generation();
let want = Data(2);
writer.write(want).now_or_never().unwrap();
writer.read(|got| assert_eq!(got, Some(&want)));
}
}