use std::pin::Pin;
use std::task::{Context, Poll};
use futures::stream::BoxStream;
use tokio::sync::mpsc;
use wasmtime::StoreContextMut;
use wasmtime::component::{
Destination, Lift, Source, StreamConsumer, StreamProducer, StreamResult, VecBuffer,
};
pub(crate) struct ItemProducer<T: Send + 'static> {
inner: BoxStream<'static, T>,
}
impl<T: Send + 'static> ItemProducer<T> {
pub(crate) fn new(inner: BoxStream<'static, T>) -> Self {
Self { inner }
}
}
impl<T: Send + Sync + 'static, D: 'static> StreamProducer<D> for ItemProducer<T> {
type Item = T;
type Buffer = VecBuffer<T>;
fn poll_produce<'a>(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
store: StoreContextMut<'a, D>,
mut dst: Destination<'a, T, VecBuffer<T>>,
finish: bool,
) -> Poll<wasmtime::Result<StreamResult>> {
let capacity = dst.remaining(store).unwrap_or(1).max(1);
let mut items = Vec::new();
while items.len() < capacity {
match self.inner.as_mut().poll_next(cx) {
Poll::Ready(Some(item)) => items.push(item),
Poll::Ready(None) => {
if !items.is_empty() {
dst.set_buffer(items.into());
}
return Poll::Ready(Ok(StreamResult::Dropped));
}
Poll::Pending => break,
}
}
if items.is_empty() {
return if finish {
Poll::Ready(Ok(StreamResult::Cancelled))
} else {
Poll::Pending
};
}
dst.set_buffer(items.into());
Poll::Ready(Ok(StreamResult::Completed))
}
}
pub(crate) struct ByteProducer {
inner: BoxStream<'static, Vec<u8>>,
}
impl ByteProducer {
pub(crate) fn new(inner: BoxStream<'static, Vec<u8>>) -> Self {
Self { inner }
}
}
impl<D: 'static> StreamProducer<D> for ByteProducer {
type Item = u8;
type Buffer = VecBuffer<u8>;
fn poll_produce<'a>(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
_store: StoreContextMut<'a, D>,
mut dst: Destination<'a, u8, VecBuffer<u8>>,
finish: bool,
) -> Poll<wasmtime::Result<StreamResult>> {
loop {
match self.inner.as_mut().poll_next(cx) {
Poll::Ready(Some(chunk)) if chunk.is_empty() => {}
Poll::Ready(Some(chunk)) => {
dst.set_buffer(chunk.into());
return Poll::Ready(Ok(StreamResult::Completed));
}
Poll::Ready(None) => return Poll::Ready(Ok(StreamResult::Dropped)),
Poll::Pending => {
return if finish {
Poll::Ready(Ok(StreamResult::Cancelled))
} else {
Poll::Pending
};
}
}
}
}
}
pub(crate) struct ChannelConsumer<T> {
tx: mpsc::UnboundedSender<T>,
}
impl<T> ChannelConsumer<T> {
pub(crate) fn new(tx: mpsc::UnboundedSender<T>) -> Self {
Self { tx }
}
}
impl<T: Lift + Send + Sync + 'static, D: 'static> StreamConsumer<D> for ChannelConsumer<T> {
type Item = T;
fn poll_consume(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
mut store: StoreContextMut<D>,
mut source: Source<'_, T>,
_finish: bool,
) -> Poll<wasmtime::Result<StreamResult>> {
if self.tx.is_closed() {
return Poll::Ready(Ok(StreamResult::Dropped));
}
let available = source.remaining(&mut store);
let mut items: Vec<T> = Vec::with_capacity(available.max(1));
source.read(&mut store, &mut items)?;
for item in items {
if self.tx.send(item).is_err() {
return Poll::Ready(Ok(StreamResult::Dropped));
}
}
Poll::Ready(Ok(StreamResult::Completed))
}
}
pub(crate) fn receiver_stream<T: Send + 'static>(
mut rx: mpsc::UnboundedReceiver<T>,
) -> BoxStream<'static, T> {
Box::pin(futures::stream::poll_fn(move |cx| rx.poll_recv(cx)))
}