cloud_sdk/transport/streaming/
policy.rs1pub const MAX_STREAM_BYTES: u64 = 1_125_899_906_842_624;
5pub const MAX_STREAM_CHUNK_BYTES: usize = 16_777_216;
7pub const MAX_STREAM_CHUNKS: u32 = 16_777_216;
9pub const MAX_STREAM_OBSERVATIONS: u32 = 67_108_864;
11pub const MAX_CONSECUTIVE_ZERO_PROGRESS: u16 = 4_096;
13
14#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub enum StreamKind {
17 FiniteUpload,
19 FiniteDownload,
21 CallerCancelledEvent,
24}
25
26#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
28pub enum StreamFraming {
29 Declared(u64),
31 ExecutorOwned,
33}
34
35#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
37pub enum StreamSinkMode {
38 Transactional,
40 Direct,
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub enum StreamLimitsError {
47 ByteLimitZero,
49 ByteLimitTooLarge,
51 ChunkBytesZero,
53 ChunkBytesTooLarge,
55 ChunkLimitZero,
57 ChunkLimitTooLarge,
59 ObservationLimitTooSmall,
61 ObservationLimitTooLarge,
63 ZeroProgressLimitTooLarge,
65}
66
67impl_static_error!(StreamLimitsError,
68 Self::ByteLimitZero => "stream byte limit is zero",
69 Self::ByteLimitTooLarge => "stream byte limit exceeds the global ceiling",
70 Self::ChunkBytesZero => "stream chunk-size limit is zero",
71 Self::ChunkBytesTooLarge => "stream chunk-size limit exceeds the global ceiling",
72 Self::ChunkLimitZero => "stream chunk limit is zero",
73 Self::ChunkLimitTooLarge => "stream chunk limit exceeds the global ceiling",
74 Self::ObservationLimitTooSmall => "stream observation limit cannot admit every chunk",
75 Self::ObservationLimitTooLarge => "stream observation limit exceeds the global ceiling",
76 Self::ZeroProgressLimitTooLarge => "stream zero-progress limit is too large",
77);
78
79#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
81pub struct StreamLimits {
82 max_bytes: u64,
83 max_chunk_bytes: usize,
84 max_chunks: u32,
85 max_observations: u32,
86 max_consecutive_zero_progress: u16,
87}
88
89impl StreamLimits {
90 pub const fn new(
92 max_bytes: u64,
93 max_chunk_bytes: usize,
94 max_chunks: u32,
95 max_observations: u32,
96 max_consecutive_zero_progress: u16,
97 ) -> Result<Self, StreamLimitsError> {
98 if max_bytes == 0 {
99 return Err(StreamLimitsError::ByteLimitZero);
100 }
101 if max_bytes > MAX_STREAM_BYTES {
102 return Err(StreamLimitsError::ByteLimitTooLarge);
103 }
104 if max_chunk_bytes == 0 {
105 return Err(StreamLimitsError::ChunkBytesZero);
106 }
107 if max_chunk_bytes > MAX_STREAM_CHUNK_BYTES {
108 return Err(StreamLimitsError::ChunkBytesTooLarge);
109 }
110 if max_chunks == 0 {
111 return Err(StreamLimitsError::ChunkLimitZero);
112 }
113 if max_chunks > MAX_STREAM_CHUNKS {
114 return Err(StreamLimitsError::ChunkLimitTooLarge);
115 }
116 if max_observations < max_chunks {
117 return Err(StreamLimitsError::ObservationLimitTooSmall);
118 }
119 if max_observations > MAX_STREAM_OBSERVATIONS {
120 return Err(StreamLimitsError::ObservationLimitTooLarge);
121 }
122 if max_consecutive_zero_progress > MAX_CONSECUTIVE_ZERO_PROGRESS
123 || max_consecutive_zero_progress as u32 > max_observations
124 {
125 return Err(StreamLimitsError::ZeroProgressLimitTooLarge);
126 }
127 Ok(Self {
128 max_bytes,
129 max_chunk_bytes,
130 max_chunks,
131 max_observations,
132 max_consecutive_zero_progress,
133 })
134 }
135
136 #[must_use]
138 pub const fn max_bytes(self) -> u64 {
139 self.max_bytes
140 }
141
142 #[must_use]
144 pub const fn max_chunk_bytes(self) -> usize {
145 self.max_chunk_bytes
146 }
147
148 #[must_use]
150 pub const fn max_chunks(self) -> u32 {
151 self.max_chunks
152 }
153
154 #[must_use]
156 pub const fn max_observations(self) -> u32 {
157 self.max_observations
158 }
159
160 #[must_use]
162 pub const fn max_consecutive_zero_progress(self) -> u16 {
163 self.max_consecutive_zero_progress
164 }
165}
166
167#[derive(Clone, Copy, Debug, Eq, PartialEq)]
169pub enum StreamPolicyError {
170 DeclaredLengthTooLarge,
172 EventRequiresExecutorFraming,
174}
175
176impl_static_error!(StreamPolicyError,
177 Self::DeclaredLengthTooLarge => "declared stream length exceeds the operation limit",
178 Self::EventRequiresExecutorFraming => "event stream requires executor-owned framing",
179);
180
181#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
183pub struct StreamPolicy {
184 kind: StreamKind,
185 framing: StreamFraming,
186 sink_mode: StreamSinkMode,
187 limits: StreamLimits,
188}
189
190impl StreamPolicy {
191 pub const fn new(
193 kind: StreamKind,
194 framing: StreamFraming,
195 sink_mode: StreamSinkMode,
196 limits: StreamLimits,
197 ) -> Result<Self, StreamPolicyError> {
198 if let StreamFraming::Declared(length) = framing
199 && length > limits.max_bytes
200 {
201 return Err(StreamPolicyError::DeclaredLengthTooLarge);
202 }
203 if matches!(kind, StreamKind::CallerCancelledEvent)
204 && !matches!(framing, StreamFraming::ExecutorOwned)
205 {
206 return Err(StreamPolicyError::EventRequiresExecutorFraming);
207 }
208 Ok(Self {
209 kind,
210 framing,
211 sink_mode,
212 limits,
213 })
214 }
215
216 #[must_use]
218 pub const fn kind(self) -> StreamKind {
219 self.kind
220 }
221
222 #[must_use]
224 pub const fn framing(self) -> StreamFraming {
225 self.framing
226 }
227
228 #[must_use]
230 pub const fn sink_mode(self) -> StreamSinkMode {
231 self.sink_mode
232 }
233
234 #[must_use]
236 pub const fn limits(self) -> StreamLimits {
237 self.limits
238 }
239}
240
241pub(super) const fn partial_state(
242 mode: StreamSinkMode,
243 write_attempted: bool,
244) -> super::StreamPartialState {
245 if !write_attempted {
246 super::StreamPartialState::Clean
247 } else {
248 match mode {
249 StreamSinkMode::Transactional => super::StreamPartialState::RollbackRequired,
250 StreamSinkMode::Direct => super::StreamPartialState::Dirty,
251 }
252 }
253}