//! A streaming Cargo response. Dropping the body cancels the builder's native process group. use crate::builder::Event; use bytes::Bytes; use http::{Request, Response}; use http_body_util::BodyExt; use std::{ pin::Pin, task::{Context, Poll}, }; struct Body(tokio::sync::mpsc::Receiver); impl http_body::Body for Body { type Data = Bytes; type Error = wasmtime_wasi_http::Error; fn poll_frame( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { self.0.poll_recv(cx).map(|event| { event.map(|event| { let mut bytes = serde_json::to_vec(&event).expect("Cargo event"); bytes.push(b'\n'); Ok(http_body::Frame::data(Bytes::from(bytes))) }) }) } } pub(crate) fn stream( api: &super::Api, request: Request, ) -> Response { let (tx, rx) = tokio::sync::mpsc::channel(16); let builder = api.builder.get().cloned(); let request = serde_json::from_slice::(request.body()); tokio::spawn(async move { let result = async { builder .ok_or_else(|| anyhow::anyhow!("builder unavailable"))? .execute(request?, &tx) .await } .await; if let Err(error) = result { let _ = tx .send(Event::Stderr { bytes: format!("cargo: {error:#}\n").into_bytes(), }) .await; let _ = tx .send(Event::Exit { code: 1, executables: Vec::new(), }) .await; } }); Response::builder() .header("content-type", "application/x-ndjson") .body(Body(rx).boxed_unsync()) .unwrap() }