use alloc::sync::Arc;
use bacnet_types::error::Error;
use super::{Event, SaveTicket, SaveWait};
use crate::prelude::*;
type SaveFn<S> = dyn Fn(&S) -> Result<(), Error> + Send + Sync;
type DoneFn<S> = dyn Fn(&S, &Result<(), Error>) + Send + Sync;
pub(crate) struct SaveWriter<S: Send + 'static> {
save: Box<SaveFn<S>>,
done: Box<DoneFn<S>>,
}
impl<S: Send + 'static> SaveWriter<S> {
pub(crate) fn new(
name: String,
save: impl Fn(&S) -> Result<(), Error> + Send + Sync + 'static,
done: impl Fn(&S, &Result<(), Error>) + Send + Sync + 'static,
) -> Self {
drop(name);
Self {
save: Box::new(save),
done: Box::new(done),
}
}
pub(crate) fn submit(&mut self, snapshot: S) -> SaveTicket {
let ticket = SaveTicket::default();
self.run(snapshot, Some(&ticket));
ticket
}
pub(crate) fn submit_coalescing(&mut self, snapshot: S) {
self.run(snapshot, None);
}
pub(crate) fn idle(&self) -> SaveWait {
let event = Arc::new(Event::default());
event.set();
SaveWait::new(event)
}
pub(crate) fn wait_idle(&self) {}
pub(crate) fn put_back(&mut self, served: S) {
let _ = self.submit(served).take_outcome();
}
fn run(&self, snapshot: S, ticket: Option<&SaveTicket>) {
let result = (self.save)(&snapshot);
(self.done)(&snapshot, &result);
drop(snapshot);
if let Some(ticket) = ticket {
ticket.complete(result);
}
}
}
#[cfg(test)]
mod tests {
use alloc::sync::Arc;
use std::sync::Mutex;
use bacnet_types::error::Error;
use super::SaveWriter;
fn writer(log: &Arc<Mutex<Vec<String>>>) -> SaveWriter<u32> {
let (saves, dones) = (Arc::clone(log), Arc::clone(log));
SaveWriter::new(
"test".into(),
move |n: &u32| {
saves.lock().unwrap().push(format!("save {n}"));
if *n == 13 {
Err(Error::Encoding("refused".into()))
} else {
Ok(())
}
},
move |n, result| {
dones
.lock()
.unwrap()
.push(format!("done {n} {}", result.is_ok()));
},
)
}
#[test]
#[should_panic(expected = "await it instead")]
fn blocking_on_a_wait_that_isnt_over_panics() {
let wait = super::SaveWait::new(Arc::new(super::Event::default()));
wait.block();
}
#[test]
fn each_save_runs_before_the_call_that_queued_it_returns() {
let log = Arc::new(Mutex::new(Vec::new()));
let mut writer = writer(&log);
let ticket = writer.submit(1);
assert!(ticket.is_done() && ticket.wait().is_ready());
assert_eq!(ticket.succeeded(), Some(true));
writer.submit_coalescing(2);
writer.submit_coalescing(3);
let failed = writer.submit(13);
assert!(failed.take_outcome().is_err());
assert!(writer.idle().is_ready());
writer.wait_idle();
writer.put_back(4);
assert_eq!(
*log.lock().unwrap(),
[
"save 1",
"done 1 true",
"save 2",
"done 2 true",
"save 3",
"done 3 true",
"save 13",
"done 13 false",
"save 4",
"done 4 true",
]
);
}
}