use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use atomic_waker::AtomicWaker;
use crossbeam_channel::Receiver;
use futures_core::Stream;
use crate::experience::{Experience, ExperienceType};
use crate::storage::schema::{WatchEventRecord, WatchEventTypeTag};
use crate::types::{CollectiveId, ExperienceId, Timestamp};
#[derive(Clone, Debug)]
pub struct WatchEvent {
pub experience_id: ExperienceId,
pub collective_id: CollectiveId,
pub event_type: WatchEventType,
pub timestamp: Timestamp,
pub experience: Option<Experience>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WatchEventType {
Created,
Updated,
Archived,
Deleted,
}
impl From<WatchEventType> for WatchEventTypeTag {
fn from(value: WatchEventType) -> Self {
match value {
WatchEventType::Created => Self::Created,
WatchEventType::Updated => Self::Updated,
WatchEventType::Archived => Self::Archived,
WatchEventType::Deleted => Self::Deleted,
}
}
}
impl From<WatchEventTypeTag> for WatchEventType {
fn from(value: WatchEventTypeTag) -> Self {
match value {
WatchEventTypeTag::Created => Self::Created,
WatchEventTypeTag::Updated => Self::Updated,
WatchEventTypeTag::Archived => Self::Archived,
WatchEventTypeTag::Deleted => Self::Deleted,
}
}
}
impl From<WatchEventRecord> for WatchEvent {
fn from(record: WatchEventRecord) -> Self {
Self {
experience_id: ExperienceId::from_bytes(record.entity_id),
collective_id: CollectiveId::from_bytes(record.collective_id),
event_type: record.event_type.into(),
timestamp: Timestamp::from_millis(record.timestamp_ms),
experience: None, }
}
}
#[derive(Clone, Debug, Default)]
pub struct WatchFilter {
pub domains: Option<Vec<String>>,
pub experience_types: Option<Vec<ExperienceType>>,
pub min_importance: Option<f32>,
}
pub struct WatchStream {
pub(crate) receiver: Receiver<WatchEvent>,
pub(crate) waker: Arc<AtomicWaker>,
pub(crate) cleanup: Option<Box<dyn FnOnce() + Send + Sync>>,
}
impl Stream for WatchStream {
type Item = WatchEvent;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.waker.register(cx.waker());
match self.receiver.try_recv() {
Ok(event) => Poll::Ready(Some(event)),
Err(crossbeam_channel::TryRecvError::Empty) => Poll::Pending,
Err(crossbeam_channel::TryRecvError::Disconnected) => Poll::Ready(None),
}
}
}
impl Drop for WatchStream {
fn drop(&mut self) {
if let Some(cleanup) = self.cleanup.take() {
cleanup();
}
}
}
impl std::fmt::Debug for WatchStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WatchStream")
.field("pending_events", &self.receiver.len())
.finish_non_exhaustive()
}
}