use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::Stream;
pub enum Outcome<I, O> {
Item(I),
Done(O),
}
pub struct AgentStream<'a, I, O> {
inner: Pin<Box<dyn Stream<Item = Outcome<I, O>> + Send + 'a>>,
result: Option<O>,
}
impl<'a, I, O> AgentStream<'a, I, O> {
pub fn new(inner: impl Stream<Item = Outcome<I, O>> + Send + 'a) -> Self {
Self {
inner: Box::pin(inner),
result: None,
}
}
pub fn take_result(&mut self) -> Option<O> {
self.result.take()
}
}
impl<I, O: Unpin> AgentStream<'_, I, O> {
pub async fn next(&mut self) -> Option<I> {
use futures_util::StreamExt;
StreamExt::next(self).await
}
}
impl<I, O: Unpin> Stream for AgentStream<'_, I, O> {
type Item = I;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<I>> {
let this = self.get_mut();
match this.inner.as_mut().poll_next(cx) {
Poll::Ready(Some(Outcome::Item(item))) => Poll::Ready(Some(item)),
Poll::Ready(Some(Outcome::Done(result))) => {
this.result = Some(result);
Poll::Ready(None)
}
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::StreamExt;
#[tokio::test]
async fn yields_items_then_result() {
let mut s = AgentStream::new(async_stream::stream! {
yield Outcome::Item("a");
yield Outcome::Item("b");
yield Outcome::Done(42);
});
let items: Vec<&str> = s.by_ref().collect().await;
assert_eq!(items, vec!["a", "b"]);
assert_eq!(s.take_result(), Some(42));
}
#[tokio::test]
async fn empty_stream_no_result() {
let mut s: AgentStream<&str, i32> = AgentStream::new(futures_util::stream::empty());
let items: Vec<&str> = s.by_ref().collect().await;
assert!(items.is_empty());
assert_eq!(s.take_result(), None);
}
#[tokio::test]
async fn result_only() {
let mut s: AgentStream<&str, i32> = AgentStream::new(async_stream::stream! {
yield Outcome::Done(99);
});
let items: Vec<&str> = s.by_ref().collect().await;
assert!(items.is_empty());
assert_eq!(s.take_result(), Some(99));
}
#[tokio::test]
async fn next_convenience() {
let mut s = AgentStream::new(async_stream::stream! {
yield Outcome::Item("x");
yield Outcome::Item("y");
yield Outcome::Done(7);
});
assert_eq!(s.next().await, Some("x"));
assert_eq!(s.next().await, Some("y"));
assert_eq!(s.next().await, None);
assert_eq!(s.take_result(), Some(7));
}
}