use std::sync::{Arc, Mutex};
use crate::document::Document;
use crate::error::TalaDbError;
use crate::query::filter::Filter;
#[derive(Clone, Debug)]
pub struct WriteEvent;
const CHANNEL_CAPACITY: usize = 64;
type Sender = std::sync::mpsc::SyncSender<WriteEvent>;
#[derive(Default)]
pub struct WatchRegistry {
senders: Vec<Sender>,
generation: u64,
}
impl WatchRegistry {
pub fn notify(&mut self) {
self.generation = self.generation.wrapping_add(1);
self.senders.retain(|tx| match tx.try_send(WriteEvent) {
Ok(()) => true,
Err(std::sync::mpsc::TrySendError::Full(_)) => true,
Err(std::sync::mpsc::TrySendError::Disconnected(_)) => false,
});
}
pub fn generation(&self) -> u64 {
self.generation
}
fn subscribe(&mut self) -> std::sync::mpsc::Receiver<WriteEvent> {
let (tx, rx) = std::sync::mpsc::sync_channel(CHANNEL_CAPACITY);
self.senders.push(tx);
rx
}
}
type QueryFn = Box<dyn Fn(&Filter) -> Result<Vec<Document>, TalaDbError> + Send>;
pub struct WatchHandle {
rx: std::sync::mpsc::Receiver<WriteEvent>,
filter: Filter,
query_fn: QueryFn,
}
impl WatchHandle {
pub fn next(&self) -> Result<Vec<Document>, TalaDbError> {
self.rx.recv().map_err(|_| TalaDbError::WatchClosed)?;
while self.rx.try_recv().is_ok() {}
(self.query_fn)(&self.filter)
}
pub fn try_next(&self) -> Result<Option<Vec<Document>>, TalaDbError> {
match self.rx.try_recv() {
Ok(_) => {
while self.rx.try_recv().is_ok() {}
Ok(Some((self.query_fn)(&self.filter)?))
}
Err(std::sync::mpsc::TryRecvError::Empty) => Ok(None),
Err(std::sync::mpsc::TryRecvError::Disconnected) => Err(TalaDbError::WatchClosed),
}
}
pub fn next_timeout(
&self,
timeout: std::time::Duration,
) -> Result<Option<Vec<Document>>, TalaDbError> {
match self.rx.recv_timeout(timeout) {
Ok(_) => {
while self.rx.try_recv().is_ok() {}
Ok(Some((self.query_fn)(&self.filter)?))
}
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => Ok(None),
Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => Err(TalaDbError::WatchClosed),
}
}
pub fn iter(&self) -> WatchIter<'_> {
WatchIter { handle: self }
}
}
pub struct WatchIter<'a> {
handle: &'a WatchHandle,
}
impl<'a> Iterator for WatchIter<'a> {
type Item = Result<Vec<Document>, TalaDbError>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.handle.next())
}
}
pub type SharedRegistry = Arc<Mutex<WatchRegistry>>;
pub fn new_registry() -> SharedRegistry {
Arc::new(Mutex::new(WatchRegistry::default()))
}
pub fn create_watch<F>(registry: &SharedRegistry, filter: Filter, query_fn: F) -> WatchHandle
where
F: Fn(&Filter) -> Result<Vec<Document>, TalaDbError> + Send + 'static,
{
let rx = registry
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.subscribe();
WatchHandle {
rx,
filter,
query_fn: Box::new(query_fn),
}
}
pub fn notify(registry: &SharedRegistry) {
let mut guard = registry
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.notify();
}
pub fn generation(registry: &SharedRegistry) -> u64 {
registry
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.generation()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Database;
#[test]
fn watch_receives_insert() {
let db = Arc::new(Database::open_in_memory().unwrap());
let registry = new_registry();
let db_clone = Arc::clone(&db);
let handle = create_watch(®istry, Filter::All, move |filter| {
let col = db_clone.collection("users").unwrap();
col.find(filter.clone())
});
notify(®istry);
let snapshot = handle.next().unwrap();
assert!(snapshot.is_empty()); }
}