use core::future::Future;
use core::pin::Pin;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("the frame could not be written to the peer")]
pub struct SinkError;
pub trait FrameSink {
fn send(&self, frame: &str) -> impl Future<Output = Result<(), SinkError>> + Send;
}
pub trait DynFrameSink: Send + Sync {
fn send<'a>(
&'a self,
frame: &'a str,
) -> Pin<Box<dyn Future<Output = Result<(), SinkError>> + Send + 'a>>;
}
impl<T> DynFrameSink for T
where
T: FrameSink + Send + Sync,
{
fn send<'a>(
&'a self,
frame: &'a str,
) -> Pin<Box<dyn Future<Output = Result<(), SinkError>> + Send + 'a>> {
Box::pin(FrameSink::send(self, frame))
}
}
#[cfg(test)]
mod tests {
use core::future::Future;
use core::pin::Pin;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::task::{Context, Poll, Waker};
use std::sync::Arc;
use super::{DynFrameSink, FrameSink, SinkError};
#[derive(Default)]
struct Counting {
written: AtomicUsize,
}
impl Counting {
fn written(&self) -> usize {
self.written.load(Ordering::Relaxed)
}
}
impl FrameSink for Counting {
fn send(&self, _frame: &str) -> impl Future<Output = Result<(), SinkError>> + Send {
self.written.fetch_add(1, Ordering::Relaxed);
core::future::ready(Ok(()))
}
}
struct Broken;
impl FrameSink for Broken {
fn send(&self, _frame: &str) -> impl Future<Output = Result<(), SinkError>> + Send {
core::future::ready(Err(SinkError))
}
}
fn drive<F: Future>(future: F) -> Option<F::Output> {
let mut context = Context::from_waker(Waker::noop());
let mut future = Box::pin(future);
match future.as_mut().poll(&mut context) {
Poll::Ready(value) => Some(value),
Poll::Pending => None,
}
}
#[test]
fn the_boxed_spelling_forwards_to_the_same_implementation() {
let concrete = Arc::new(Counting::default());
assert_eq!(drive(FrameSink::send(&*concrete, "{}")), Some(Ok(())));
let boxed: Arc<dyn DynFrameSink> = concrete.clone();
assert_eq!(drive(boxed.send("{}")), Some(Ok(())));
assert_eq!(
concrete.written(),
2,
"the blanket impl must not lose a frame"
);
}
#[test]
fn a_failing_sink_reports_the_error_through_both_spellings() {
let concrete = Broken;
assert_eq!(
drive(FrameSink::send(&concrete, "{}")),
Some(Err(SinkError))
);
let boxed: Arc<dyn DynFrameSink> = Arc::new(Broken);
assert_eq!(drive(boxed.send("{}")), Some(Err(SinkError)));
}
#[test]
fn a_boxed_sink_is_usable_from_behind_an_arc() {
fn write(
sink: &Arc<dyn DynFrameSink>,
) -> Pin<Box<dyn Future<Output = Result<(), SinkError>> + Send + '_>> {
sink.send("{\"jsonrpc\":\"2.0\"}")
}
let sink: Arc<dyn DynFrameSink> = Arc::new(Counting::default());
assert_eq!(drive(write(&sink)), Some(Ok(())));
}
}