use tokio::sync::mpsc;
use super::{Event, EventSender};
use crate::cli::output::Handle;
pub fn cli_event_sink(events_tx: EventSender, destination: String) -> Handle {
let (tx, mut rx) = mpsc::unbounded_channel();
let handle = Handle::Stream(tx);
tokio::spawn(async move {
while let Some(output) = rx.recv().await {
let value = serde_json::to_value(&output).unwrap_or(serde_json::Value::Null);
if events_tx
.send(Event::CliCommand {
destination: destination.clone(),
value,
})
.is_err()
{
break;
}
}
});
handle
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::output::Output;
use serde_json::json;
#[tokio::test]
async fn forwards_each_output_as_cli_command_event() {
let (events_tx, mut events_rx) = mpsc::unbounded_channel();
let handle = cli_event_sink(events_tx, "my_plugin".to_string());
Output::<serde_json::Value>::Begin.emit(&handle).await;
Output::<serde_json::Value>::Notification(crate::cli::output::Notification {
value: json!({"x": 1}),
})
.emit(&handle)
.await;
Output::<serde_json::Value>::End.emit(&handle).await;
drop(handle);
let mut received = Vec::new();
while let Some(event) = events_rx.recv().await {
received.push(event);
}
assert_eq!(received.len(), 3);
for event in &received {
match event {
Event::CliCommand { destination, .. } => {
assert_eq!(destination, "my_plugin");
}
other => panic!("expected CliCommand, got {other:?}"),
}
}
let Event::CliCommand { value: v0, .. } = &received[0] else {
unreachable!()
};
assert_eq!(v0["type"], "begin");
let Event::CliCommand { value: v1, .. } = &received[1] else {
unreachable!()
};
assert_eq!(v1["type"], "notification");
assert_eq!(v1["value"], json!({"x": 1}));
let Event::CliCommand { value: v2, .. } = &received[2] else {
unreachable!()
};
assert_eq!(v2["type"], "end");
}
#[tokio::test]
async fn dropping_events_receiver_stops_forwarder() {
let (events_tx, events_rx) = mpsc::unbounded_channel();
let handle = cli_event_sink(events_tx, "p".to_string());
drop(events_rx);
Output::<serde_json::Value>::End.emit(&handle).await;
tokio::task::yield_now().await;
drop(handle);
}
}