use std::{
fmt::Debug,
ops::{Deref, DerefMut},
pin::Pin,
sync::Arc,
};
use crate::{Entry, entry::BoxEntry};
pub trait EntrySink<E: Entry> {
fn append(&self, entry: E);
fn flush_async(&self) -> FlushWait;
fn append_on_drop(&self, entry: E) -> AppendOnDrop<E, Self>
where
Self: Sized + Clone,
{
AppendOnDrop::new(entry, self.clone())
}
fn append_on_drop_default(&self) -> AppendOnDrop<E, Self>
where
Self: Sized + Clone,
E: Default,
{
self.append_on_drop(E::default())
}
}
pub trait AnyEntrySink {
fn append_any(&self, entry: impl Entry + Send + 'static);
fn flush_async(&self) -> FlushWait;
fn boxed(self) -> BoxEntrySink
where
Self: Sized + Send + Sync + 'static,
{
BoxEntrySink::new(self)
}
}
impl<T: AnyEntrySink, E: Entry + Send + 'static> EntrySink<E> for T {
fn flush_async(&self) -> FlushWait {
AnyEntrySink::flush_async(self)
}
fn append(&self, entry: E) {
self.append_any(entry)
}
}
#[derive(Clone)]
pub struct BoxEntrySink(Arc<Box<dyn EntrySink<BoxEntry> + Send + Sync + 'static>>);
impl Debug for BoxEntrySink {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("BoxEntrySink").finish()
}
}
impl AnyEntrySink for BoxEntrySink {
fn append_any(&self, entry: impl Entry + Send + 'static) {
self.0.append(entry.boxed())
}
fn flush_async(&self) -> FlushWait {
self.0.flush_async()
}
}
impl BoxEntrySink {
pub fn new(sink: impl EntrySink<BoxEntry> + Send + Sync + 'static) -> Self {
Self(Arc::new(Box::new(sink)))
}
pub fn lazy(factory: impl Fn() -> Option<BoxEntrySink> + Send + Sync + 'static) -> Self {
Self::new(LazySink(Arc::new(factory)))
}
}
struct LazySink(Arc<dyn Fn() -> Option<BoxEntrySink> + Send + Sync>);
impl EntrySink<BoxEntry> for LazySink {
fn append(&self, entry: BoxEntry) {
if let Some(sink) = (self.0)() {
sink.0.append(entry);
}
}
fn flush_async(&self) -> FlushWait {
match (self.0)() {
Some(sink) => sink.0.flush_async(),
None => FlushWait::ready(),
}
}
}
#[must_use = "future does nothing unless polled"]
pub struct FlushWait(Pin<Box<dyn std::future::Future<Output = ()> + Send + Sync + 'static>>);
impl Future for FlushWait {
type Output = ();
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
self.0.as_mut().poll(cx)
}
}
impl FlushWait {
pub fn ready() -> Self {
Self(Box::pin(std::future::poll_fn(|_| {
std::task::Poll::Ready(())
})))
}
pub fn from_future(f: impl std::future::Future<Output = ()> + Send + Sync + 'static) -> Self {
Self(Box::pin(f))
}
}
#[derive(Debug, Clone)]
pub struct AppendOnDrop<E: Entry, Q: EntrySink<E>> {
entry: Option<E>,
sink: Q,
}
impl<E: Entry, Q: EntrySink<E>> AppendOnDrop<E, Q> {
pub(crate) fn new(entry: E, sink: Q) -> Self {
Self {
entry: Some(entry),
sink,
}
}
}
impl<E: Entry, Q: EntrySink<E>> Drop for AppendOnDrop<E, Q> {
fn drop(&mut self) {
if let Some(entry) = self.entry.take() {
self.sink.append(entry)
}
}
}
impl<E: Entry, Q: EntrySink<E>> AppendOnDrop<E, Q> {
pub fn into_entry(mut self) -> E {
self.entry.take().unwrap()
}
pub fn forget(mut self) {
self.entry = None;
}
}
impl<E: Entry, Q: EntrySink<E>> Deref for AppendOnDrop<E, Q> {
type Target = E;
fn deref(&self) -> &Self::Target {
self.entry.as_ref().unwrap()
}
}
impl<E: Entry, Q: EntrySink<E>> DerefMut for AppendOnDrop<E, Q> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.entry.as_mut().unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_stream::TestEntry;
use std::sync::{Arc, Mutex};
#[test]
fn lazy_sink_resolves_at_append_time() {
let inner: Arc<Mutex<Option<BoxEntrySink>>> = Arc::new(Mutex::new(None));
let inner_clone = inner.clone();
let sink = BoxEntrySink::lazy(move || inner_clone.lock().unwrap().clone());
sink.append_any(TestEntry(1));
let flush_before_attach = AnyEntrySink::flush_async(&sink);
drop(flush_before_attach);
let appended = Arc::new(Mutex::new(Vec::new()));
let appended_clone = appended.clone();
let flushes = Arc::new(Mutex::new(0));
let flushes_clone = flushes.clone();
struct CollectorSink {
appended: Arc<Mutex<Vec<u64>>>,
flushes: Arc<Mutex<u64>>,
}
impl EntrySink<BoxEntry> for CollectorSink {
fn append(&self, _entry: BoxEntry) {
self.appended.lock().unwrap().push(1);
}
fn flush_async(&self) -> FlushWait {
*self.flushes.lock().unwrap() += 1;
FlushWait::ready()
}
}
*inner.lock().unwrap() = Some(BoxEntrySink::new(CollectorSink {
appended: appended_clone,
flushes: flushes_clone,
}));
sink.append_any(TestEntry(2));
let flush_after_attach = AnyEntrySink::flush_async(&sink);
drop(flush_after_attach);
assert_eq!(appended.lock().unwrap().len(), 1);
assert_eq!(*flushes.lock().unwrap(), 1);
}
}