cloud_sdk/transport/streaming/
io.rs1mod asynchronous;
4mod blocking;
5
6pub use asynchronous::{drive_async_stream, drive_local_stream};
7pub use blocking::drive_blocking_stream;
8
9use cloud_sdk_sanitization::sanitize_bytes;
10use core::{fmt, future::Future};
11
12use super::policy::partial_state;
13use super::{StreamPartialState, StreamPolicy, StreamProgressError, StreamReplayability};
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub enum StreamRead {
18 Chunk(usize),
20 Wait,
22 End,
24}
25
26pub trait BlockingStreamSource {
28 type Error;
30
31 fn replayability(&self) -> StreamReplayability<'_>;
33
34 fn read_chunk(&mut self, output: &mut [u8]) -> Result<StreamRead, Self::Error>;
36}
37
38pub trait BlockingStreamSink {
40 type Error;
42
43 fn write_chunk(&mut self, input: &[u8]) -> Result<usize, Self::Error>;
45
46 fn commit(&mut self) -> Result<(), Self::Error>;
48
49 fn abort(&mut self, partial: StreamPartialState);
51}
52
53pub trait LocalAsyncStreamSource {
55 type Error;
57
58 fn replayability(&self) -> StreamReplayability<'_>;
60
61 fn read_chunk_local<'operation>(
63 &'operation mut self,
64 output: &'operation mut [u8],
65 ) -> impl Future<Output = Result<StreamRead, Self::Error>> + 'operation;
66}
67
68pub trait AsyncStreamSource {
70 type Error;
72
73 fn replayability(&self) -> StreamReplayability<'_>;
75
76 fn read_chunk<'operation>(
78 &'operation mut self,
79 output: &'operation mut [u8],
80 ) -> impl Future<Output = Result<StreamRead, Self::Error>> + Send + 'operation;
81}
82
83impl<T: AsyncStreamSource> LocalAsyncStreamSource for T {
84 type Error = T::Error;
85
86 fn replayability(&self) -> StreamReplayability<'_> {
87 AsyncStreamSource::replayability(self)
88 }
89
90 async fn read_chunk_local<'operation>(
91 &'operation mut self,
92 output: &'operation mut [u8],
93 ) -> Result<StreamRead, Self::Error> {
94 AsyncStreamSource::read_chunk(self, output).await
95 }
96}
97
98pub trait LocalAsyncStreamSink {
100 type Error;
102
103 fn write_chunk_local<'operation>(
105 &'operation mut self,
106 input: &'operation [u8],
107 ) -> impl Future<Output = Result<usize, Self::Error>> + 'operation;
108
109 fn commit_local(&mut self) -> impl Future<Output = Result<(), Self::Error>> + '_;
111
112 fn abort_local(&mut self, partial: StreamPartialState);
114}
115
116pub trait AsyncStreamSink {
118 type Error;
120
121 fn write_chunk<'operation>(
123 &'operation mut self,
124 input: &'operation [u8],
125 ) -> impl Future<Output = Result<usize, Self::Error>> + Send + 'operation;
126
127 fn commit(&mut self) -> impl Future<Output = Result<(), Self::Error>> + Send + '_;
129
130 fn abort(&mut self, partial: StreamPartialState);
132}
133
134impl<T: AsyncStreamSink> LocalAsyncStreamSink for T {
135 type Error = T::Error;
136
137 async fn write_chunk_local<'operation>(
138 &'operation mut self,
139 input: &'operation [u8],
140 ) -> Result<usize, Self::Error> {
141 AsyncStreamSink::write_chunk(self, input).await
142 }
143
144 async fn commit_local(&mut self) -> Result<(), Self::Error> {
145 AsyncStreamSink::commit(self).await
146 }
147
148 fn abort_local(&mut self, partial: StreamPartialState) {
149 AsyncStreamSink::abort(self, partial);
150 }
151}
152
153#[derive(Clone, Copy, Eq, PartialEq)]
155pub enum StreamExecutionError<S, D> {
156 EmptyScratch,
158 InvalidSourceLength,
160 Progress(StreamProgressError),
162 Source(S),
164 Sink(D),
166}
167
168impl<S, D> fmt::Debug for StreamExecutionError<S, D> {
169 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::EmptyScratch => formatter.write_str("EmptyScratch"),
172 Self::InvalidSourceLength => formatter.write_str("InvalidSourceLength"),
173 Self::Progress(error) => formatter.debug_tuple("Progress").field(error).finish(),
174 Self::Source(_) => formatter.write_str("Source([redacted])"),
175 Self::Sink(_) => formatter.write_str("Sink([redacted])"),
176 }
177 }
178}
179
180impl<S, D> fmt::Display for StreamExecutionError<S, D> {
181 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
182 formatter.write_str(match self {
183 Self::EmptyScratch => "stream scratch storage is empty",
184 Self::InvalidSourceLength => "stream source reported an invalid length",
185 Self::Progress(_) => "stream progress policy rejected execution",
186 Self::Source(_) => "stream source failed",
187 Self::Sink(_) => "stream sink failed",
188 })
189 }
190}
191
192impl<S, D> core::error::Error for StreamExecutionError<S, D> {}
193
194struct AbortGuard<'sink, S> {
195 sink: &'sink mut S,
196 partial: StreamPartialState,
197 abort: fn(&mut S, StreamPartialState),
198 armed: bool,
199}
200
201impl<'sink, S> AbortGuard<'sink, S> {
202 fn new(sink: &'sink mut S, abort: fn(&mut S, StreamPartialState)) -> Self {
203 Self {
204 sink,
205 partial: StreamPartialState::Clean,
206 abort,
207 armed: true,
208 }
209 }
210
211 fn sink(&mut self) -> &mut S {
212 self.sink
213 }
214
215 fn record_write_attempt(&mut self, policy: StreamPolicy) {
216 self.partial = partial_state(policy.sink_mode(), true);
217 }
218
219 fn disarm(&mut self) {
220 self.armed = false;
221 }
222}
223
224impl<S> Drop for AbortGuard<'_, S> {
225 fn drop(&mut self) {
226 if self.armed {
227 (self.abort)(self.sink, self.partial);
228 }
229 }
230}
231
232struct ScratchGuard<'scratch> {
233 bytes: &'scratch mut [u8],
234}
235
236impl<'scratch> ScratchGuard<'scratch> {
237 fn new(bytes: &'scratch mut [u8]) -> Self {
238 sanitize_bytes(bytes);
239 Self { bytes }
240 }
241
242 fn bytes(&mut self) -> &mut [u8] {
243 self.bytes
244 }
245}
246
247impl Drop for ScratchGuard<'_> {
248 fn drop(&mut self) {
249 sanitize_bytes(self.bytes);
250 }
251}