use std::fmt::{Debug, Display};
use serde::Serialize;
use crate::event::{Event, EventSender, SendError};
#[derive(Clone, Debug)]
pub struct Output {
sender: EventSender,
}
impl Output {
pub fn new(sender: EventSender) -> Self {
Self { sender }
}
pub async fn message(&self, message: impl Display) -> Result<(), SendError> {
self.sender.send(Event::Message(message.to_string())).await
}
pub async fn detail(&self, detail: impl Display) -> Result<(), SendError> {
self.sender.send(Event::Detail(detail.to_string())).await
}
pub async fn artifact<T>(&self, value: T) -> Result<(), SendError>
where
T: Display + Serialize + Debug + Send + Sync + 'static,
{
self.sender.send(Event::Artifact(Box::new(value))).await
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::missing_panics_doc)]
use std::fmt;
use serde::Serialize;
use super::*;
use crate::event::event_channel;
#[derive(Clone, Debug, Serialize)]
struct TestArtifact {
value: String,
}
impl fmt::Display for TestArtifact {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
#[tokio::test]
async fn artifact_sends_artifact_event() {
let (sender, mut receiver) = event_channel();
let output = Output::new(sender);
output
.artifact(TestArtifact {
value: "result".to_string(),
})
.await
.expect("should send");
let event = receiver.recv().await.expect("should receive");
assert!(matches!(event, Event::Artifact(ref a) if a.to_string() == "result"));
}
#[tokio::test]
async fn artifact_to_closed_channel_returns_error() {
let (sender, receiver) = event_channel();
let output = Output::new(sender);
drop(receiver);
let error = output
.artifact(TestArtifact {
value: "lost".to_string(),
})
.await
.expect_err("should fail");
assert_eq!(error.to_string(), "event channel closed");
}
#[tokio::test]
async fn clone_produces_independent_handle() {
let (sender, mut receiver) = event_channel();
let output = Output::new(sender);
let cloned = output.clone();
output.message("from original").await.expect("should send");
cloned.message("from clone").await.expect("should send");
let first = receiver.recv().await.expect("should receive first");
let second = receiver.recv().await.expect("should receive second");
assert!(matches!(first, Event::Message(ref s) if s == "from original"));
assert!(matches!(second, Event::Message(ref s) if s == "from clone"));
}
#[tokio::test]
async fn detail_sends_detail_event() {
let (sender, mut receiver) = event_channel();
let output = Output::new(sender);
output
.detail("supplementary info")
.await
.expect("should send");
let event = receiver.recv().await.expect("should receive");
assert!(matches!(event, Event::Detail(ref s) if s == "supplementary info"));
}
#[tokio::test]
async fn detail_to_closed_channel_returns_error() {
let (sender, receiver) = event_channel();
let output = Output::new(sender);
drop(receiver);
let error = output.detail("lost").await.expect_err("should fail");
assert_eq!(error.to_string(), "event channel closed");
}
#[tokio::test]
async fn message_sends_message_event() {
let (sender, mut receiver) = event_channel();
let output = Output::new(sender);
output.message("hello").await.expect("should send");
let event = receiver.recv().await.expect("should receive");
assert!(matches!(event, Event::Message(ref s) if s == "hello"));
}
#[tokio::test]
async fn message_to_closed_channel_returns_error() {
let (sender, receiver) = event_channel();
let output = Output::new(sender);
drop(receiver);
let error = output.message("lost").await.expect_err("should fail");
assert_eq!(error.to_string(), "event channel closed");
}
#[tokio::test]
async fn message_with_format_args_sends_formatted_string() {
let (sender, mut receiver) = event_channel();
let output = Output::new(sender);
output
.message(format!("count: {}", 42))
.await
.expect("should send");
let event = receiver.recv().await.expect("should receive");
assert!(matches!(event, Event::Message(ref s) if s == "count: 42"));
}
#[test]
fn trait_send() {
fn assert_send<T: Send>() {}
assert_send::<Output>();
}
#[test]
fn trait_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Output>();
}
#[test]
fn trait_unpin() {
fn assert_unpin<T: Unpin>() {}
assert_unpin::<Output>();
}
}