mod asynchronous;
mod blocking;
pub use asynchronous::{drive_async_stream, drive_local_stream};
pub use blocking::drive_blocking_stream;
use cloud_sdk_sanitization::sanitize_bytes;
use core::{fmt, future::Future};
use super::policy::partial_state;
use super::{StreamPartialState, StreamPolicy, StreamProgressError, StreamReplayability};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum StreamRead {
Chunk(usize),
Wait,
End,
}
pub trait BlockingStreamSource {
type Error;
fn replayability(&self) -> StreamReplayability<'_>;
fn read_chunk(&mut self, output: &mut [u8]) -> Result<StreamRead, Self::Error>;
}
pub trait BlockingStreamSink {
type Error;
fn write_chunk(&mut self, input: &[u8]) -> Result<usize, Self::Error>;
fn commit(&mut self) -> Result<(), Self::Error>;
fn abort(&mut self, partial: StreamPartialState);
}
pub trait LocalAsyncStreamSource {
type Error;
fn replayability(&self) -> StreamReplayability<'_>;
fn read_chunk_local<'operation>(
&'operation mut self,
output: &'operation mut [u8],
) -> impl Future<Output = Result<StreamRead, Self::Error>> + 'operation;
}
pub trait AsyncStreamSource {
type Error;
fn replayability(&self) -> StreamReplayability<'_>;
fn read_chunk<'operation>(
&'operation mut self,
output: &'operation mut [u8],
) -> impl Future<Output = Result<StreamRead, Self::Error>> + Send + 'operation;
}
impl<T: AsyncStreamSource> LocalAsyncStreamSource for T {
type Error = T::Error;
fn replayability(&self) -> StreamReplayability<'_> {
AsyncStreamSource::replayability(self)
}
async fn read_chunk_local<'operation>(
&'operation mut self,
output: &'operation mut [u8],
) -> Result<StreamRead, Self::Error> {
AsyncStreamSource::read_chunk(self, output).await
}
}
pub trait LocalAsyncStreamSink {
type Error;
fn write_chunk_local<'operation>(
&'operation mut self,
input: &'operation [u8],
) -> impl Future<Output = Result<usize, Self::Error>> + 'operation;
fn commit_local(&mut self) -> impl Future<Output = Result<(), Self::Error>> + '_;
fn abort_local(&mut self, partial: StreamPartialState);
}
pub trait AsyncStreamSink {
type Error;
fn write_chunk<'operation>(
&'operation mut self,
input: &'operation [u8],
) -> impl Future<Output = Result<usize, Self::Error>> + Send + 'operation;
fn commit(&mut self) -> impl Future<Output = Result<(), Self::Error>> + Send + '_;
fn abort(&mut self, partial: StreamPartialState);
}
impl<T: AsyncStreamSink> LocalAsyncStreamSink for T {
type Error = T::Error;
async fn write_chunk_local<'operation>(
&'operation mut self,
input: &'operation [u8],
) -> Result<usize, Self::Error> {
AsyncStreamSink::write_chunk(self, input).await
}
async fn commit_local(&mut self) -> Result<(), Self::Error> {
AsyncStreamSink::commit(self).await
}
fn abort_local(&mut self, partial: StreamPartialState) {
AsyncStreamSink::abort(self, partial);
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum StreamExecutionError<S, D> {
EmptyScratch,
InvalidSourceLength,
Progress(StreamProgressError),
Source(S),
Sink(D),
}
impl<S, D> fmt::Debug for StreamExecutionError<S, D> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyScratch => formatter.write_str("EmptyScratch"),
Self::InvalidSourceLength => formatter.write_str("InvalidSourceLength"),
Self::Progress(error) => formatter.debug_tuple("Progress").field(error).finish(),
Self::Source(_) => formatter.write_str("Source([redacted])"),
Self::Sink(_) => formatter.write_str("Sink([redacted])"),
}
}
}
impl<S, D> fmt::Display for StreamExecutionError<S, D> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::EmptyScratch => "stream scratch storage is empty",
Self::InvalidSourceLength => "stream source reported an invalid length",
Self::Progress(_) => "stream progress policy rejected execution",
Self::Source(_) => "stream source failed",
Self::Sink(_) => "stream sink failed",
})
}
}
impl<S, D> core::error::Error for StreamExecutionError<S, D> {}
struct AbortGuard<'sink, S> {
sink: &'sink mut S,
partial: StreamPartialState,
abort: fn(&mut S, StreamPartialState),
armed: bool,
}
impl<'sink, S> AbortGuard<'sink, S> {
fn new(sink: &'sink mut S, abort: fn(&mut S, StreamPartialState)) -> Self {
Self {
sink,
partial: StreamPartialState::Clean,
abort,
armed: true,
}
}
fn sink(&mut self) -> &mut S {
self.sink
}
fn record_write_attempt(&mut self, policy: StreamPolicy) {
self.partial = partial_state(policy.sink_mode(), true);
}
fn disarm(&mut self) {
self.armed = false;
}
}
impl<S> Drop for AbortGuard<'_, S> {
fn drop(&mut self) {
if self.armed {
(self.abort)(self.sink, self.partial);
}
}
}
struct ScratchGuard<'scratch> {
bytes: &'scratch mut [u8],
}
impl<'scratch> ScratchGuard<'scratch> {
fn new(bytes: &'scratch mut [u8]) -> Self {
sanitize_bytes(bytes);
Self { bytes }
}
fn bytes(&mut self) -> &mut [u8] {
self.bytes
}
}
impl Drop for ScratchGuard<'_> {
fn drop(&mut self) {
sanitize_bytes(self.bytes);
}
}