use futures_util::StreamExt;
use serde::{Deserialize, Serialize};
use zlink::{
Reply, Server, introspect,
tokio::unix::{bind, connect},
};
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn streaming_errors() -> Result<(), Box<dyn std::error::Error>> {
let dir = tempfile::tempdir()?;
let socket_path = dir.path().join("test.sock");
let listener = bind(&socket_path).unwrap();
let server = Server::new(listener, CountingService);
tokio::select! {
res = server.run() => res?,
res = run_client(&socket_path) => res?,
}
Ok(())
}
async fn run_client(socket_path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
let mut conn = connect(socket_path).await?;
{
let mut stream = std::pin::pin!(conn.count(3).await?);
let mut values = Vec::new();
while let Some(result) = stream.next().await {
let tick = result?.expect("no error expected");
values.push(tick.value);
}
assert_eq!(values, vec![1, 2, 3]);
}
{
let mut stream = std::pin::pin!(conn.count(0).await?);
let mut got_error = false;
let mut other_items = 0;
while let Some(result) = stream.next().await {
match result? {
Ok(_) => other_items += 1,
Err(CountError::AtZero) => got_error = true,
Err(other) => panic!("unexpected error: {other:?}"),
}
}
assert!(got_error, "expected AtZero error from stream");
assert_eq!(other_items, 0);
}
Ok(())
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, introspect::Type)]
struct Tick {
value: u32,
}
#[derive(Debug, Clone, PartialEq, zlink::ReplyError, introspect::ReplyError)]
#[zlink(interface = "org.example.counter")]
enum CountError {
AtZero,
BadInput { reason: String },
}
struct CountingService;
#[zlink::service(interface = "org.example.counter")]
impl CountingService {
#[zlink(more)]
async fn count(
&self,
more: bool,
to: u32,
) -> impl futures_util::Stream<Item = Result<Reply<Tick>, CountError>> + Unpin {
if to == 0 {
return futures_util::stream::iter(vec![Err(CountError::AtZero)]);
}
let to = if more { to } else { 1 };
let last = to;
let items: Vec<Result<Reply<Tick>, CountError>> = (1..=to)
.map(
move |value| Ok(Reply::new(Some(Tick { value })).set_continues(Some(value < last))),
)
.collect();
futures_util::stream::iter(items)
}
}
#[zlink::proxy("org.example.counter")]
trait CountingProxy {
#[zlink(more)]
async fn count(
&mut self,
to: u32,
) -> zlink::Result<impl futures_util::Stream<Item = zlink::Result<Result<Tick, CountError>>>>;
}