use serde::{Deserialize, Serialize};
use crate::error::ClientError;
use crate::runtime::wire::{RuntimeErrorCode, RuntimeErrorWire};
use crate::streaming::CborSeqChunkDecoder;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RuntimeChunkWire {
Item(Vec<u8>),
End,
Error(RuntimeErrorWire),
}
pub(crate) async fn pump_streamed_response_callback<F>(
response: reqwest::Response,
mut on_chunk: F,
) -> Result<(), RuntimeErrorWire>
where
F: FnMut(RuntimeChunkWire) -> bool,
{
use futures_util::StreamExt;
let mut byte_stream = response.bytes_stream();
let mut decoder = CborSeqChunkDecoder::new();
loop {
let chunk_result = byte_stream.next().await;
let chunk = match chunk_result {
Some(Ok(c)) => c,
Some(Err(error)) => {
let err = RuntimeErrorWire::from(ClientError::Transport(error));
on_chunk(RuntimeChunkWire::Error(err.clone()));
return Err(err);
}
None => {
if decoder.pending_len() > 0 {
let err = RuntimeErrorWire {
code: RuntimeErrorCode::InvalidResponse,
http_status: None,
message: format!(
"stream ended with {} bytes buffered (incomplete final item)",
decoder.pending_len(),
),
remote_code: None,
remote_body: None,
};
on_chunk(RuntimeChunkWire::Error(err.clone()));
return Err(err);
}
on_chunk(RuntimeChunkWire::End);
return Ok(());
}
};
let items = match decoder.feed_chunk(&chunk) {
Ok(items) => items,
Err(error) => {
let err = RuntimeErrorWire::from(ClientError::Codec(error));
on_chunk(RuntimeChunkWire::Error(err.clone()));
return Err(err);
}
};
for item_bytes in items {
if !on_chunk(RuntimeChunkWire::Item(item_bytes)) {
return Ok(());
}
}
}
}