cloud_sdk/async_resource/
model.rs1use core::{cmp::Ordering, fmt};
2
3use crate::action_polling::ActionUpdate;
4
5use super::{
6 AsyncResourceId, AsyncResourceLink, AsyncResourceText, AsyncResourceTimestamp,
7 AsyncResourceValidationError, MAX_ASYNC_ERRORS, MAX_ASYNC_EVENTS, MAX_ASYNC_PROGRESS_STEPS,
8};
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum AsyncResourceStatus {
13 Pending,
15 Scheduled,
17 Running,
19 WaitingForInput,
21 Succeeded,
23 Failed,
25}
26
27#[derive(Debug, Eq, PartialEq)]
29pub enum AsyncPollDisposition<'a> {
30 Update(ActionUpdate<&'a [AsyncTaskError<'a>]>),
32 WaitingForInput,
34 ContradictorySuccess(&'a [AsyncTaskError<'a>]),
36}
37
38impl AsyncResourceStatus {
39 #[must_use]
41 pub const fn is_terminal(self) -> bool {
42 matches!(self, Self::Succeeded | Self::Failed)
43 }
44}
45
46#[derive(Clone, Copy, Eq, PartialEq)]
48pub struct AsyncProgressStep<'a> {
49 name: AsyncResourceText<'a>,
50 status: AsyncResourceStatus,
51}
52
53impl<'a> AsyncProgressStep<'a> {
54 #[must_use]
56 pub const fn new(name: AsyncResourceText<'a>, status: AsyncResourceStatus) -> Self {
57 Self { name, status }
58 }
59
60 #[must_use]
62 pub const fn name(self) -> AsyncResourceText<'a> {
63 self.name
64 }
65
66 #[must_use]
68 pub const fn status(self) -> AsyncResourceStatus {
69 self.status
70 }
71}
72
73impl fmt::Debug for AsyncProgressStep<'_> {
74 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
75 formatter
76 .debug_struct("AsyncProgressStep")
77 .field("name", &"[redacted]")
78 .field("status", &self.status)
79 .finish()
80 }
81}
82
83#[derive(Clone, Copy, Eq, PartialEq)]
85pub struct AsyncTaskError<'a> {
86 message: AsyncResourceText<'a>,
87}
88
89impl<'a> AsyncTaskError<'a> {
90 #[must_use]
92 pub const fn new(message: AsyncResourceText<'a>) -> Self {
93 Self { message }
94 }
95
96 #[must_use]
98 pub const fn message(self) -> AsyncResourceText<'a> {
99 self.message
100 }
101}
102
103impl fmt::Debug for AsyncTaskError<'_> {
104 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105 formatter.write_str("AsyncTaskError([redacted])")
106 }
107}
108
109#[derive(Clone, Copy)]
111pub struct AsyncTaskParts<'a> {
112 pub id: AsyncResourceId<'a>,
114 pub kind: AsyncResourceText<'a>,
116 pub status: AsyncResourceStatus,
118 pub link: Option<AsyncResourceLink<'a>>,
120 pub message: Option<AsyncResourceText<'a>>,
122 pub created_at: AsyncResourceTimestamp<'a>,
124 pub updated_at: AsyncResourceTimestamp<'a>,
126 pub started_at: Option<AsyncResourceTimestamp<'a>>,
128 pub finished_at: Option<AsyncResourceTimestamp<'a>>,
130 pub progress: &'a [AsyncProgressStep<'a>],
132 pub errors: &'a [AsyncTaskError<'a>],
134}
135
136pub struct AsyncTask<'a> {
138 parts: AsyncTaskParts<'a>,
139}
140
141impl<'a> AsyncTask<'a> {
142 pub fn new(parts: AsyncTaskParts<'a>) -> Result<Self, AsyncResourceValidationError> {
144 if parts.progress.len() > MAX_ASYNC_PROGRESS_STEPS {
145 return Err(AsyncResourceValidationError::TooManyProgressSteps);
146 }
147 if parts.errors.len() > MAX_ASYNC_ERRORS {
148 return Err(AsyncResourceValidationError::TooManyErrors);
149 }
150 if parts.updated_at.compare(parts.created_at) == Ordering::Less
151 || parts
152 .started_at
153 .is_some_and(|value| value.compare(parts.created_at) == Ordering::Less)
154 || parts
155 .started_at
156 .is_some_and(|value| value.compare(parts.updated_at) == Ordering::Greater)
157 || parts
158 .finished_at
159 .is_some_and(|value| value.compare(parts.created_at) == Ordering::Less)
160 || parts
161 .finished_at
162 .is_some_and(|value| value.compare(parts.updated_at) == Ordering::Greater)
163 || matches!((parts.started_at, parts.finished_at), (Some(started), Some(finished))
164 if finished.compare(started) == Ordering::Less)
165 {
166 return Err(AsyncResourceValidationError::TimestampOrder);
167 }
168 if parts.status.is_terminal() != parts.finished_at.is_some() {
169 return Err(AsyncResourceValidationError::TerminalTimeMismatch);
170 }
171 Ok(Self { parts })
172 }
173
174 #[must_use]
176 pub const fn id(&self) -> AsyncResourceId<'a> {
177 self.parts.id
178 }
179
180 #[must_use]
182 pub const fn kind(&self) -> AsyncResourceText<'a> {
183 self.parts.kind
184 }
185
186 #[must_use]
188 pub const fn status(&self) -> AsyncResourceStatus {
189 self.parts.status
190 }
191
192 #[must_use]
194 pub const fn link(&self) -> Option<AsyncResourceLink<'a>> {
195 self.parts.link
196 }
197
198 #[must_use]
200 pub const fn message(&self) -> Option<AsyncResourceText<'a>> {
201 self.parts.message
202 }
203
204 #[must_use]
206 pub const fn created_at(&self) -> AsyncResourceTimestamp<'a> {
207 self.parts.created_at
208 }
209
210 #[must_use]
212 pub const fn updated_at(&self) -> AsyncResourceTimestamp<'a> {
213 self.parts.updated_at
214 }
215
216 #[must_use]
218 pub const fn started_at(&self) -> Option<AsyncResourceTimestamp<'a>> {
219 self.parts.started_at
220 }
221
222 #[must_use]
224 pub const fn finished_at(&self) -> Option<AsyncResourceTimestamp<'a>> {
225 self.parts.finished_at
226 }
227
228 #[must_use]
230 pub const fn progress(&self) -> &'a [AsyncProgressStep<'a>] {
231 self.parts.progress
232 }
233
234 #[must_use]
236 pub const fn errors(&self) -> &'a [AsyncTaskError<'a>] {
237 self.parts.errors
238 }
239
240 #[must_use]
242 pub fn poll_disposition(&self) -> AsyncPollDisposition<'a> {
243 match self.parts.status {
244 AsyncResourceStatus::Succeeded if !self.parts.errors.is_empty() => {
245 AsyncPollDisposition::ContradictorySuccess(self.parts.errors)
246 }
247 AsyncResourceStatus::Succeeded => AsyncPollDisposition::Update(ActionUpdate::Success),
248 AsyncResourceStatus::Failed => {
249 AsyncPollDisposition::Update(ActionUpdate::Failed(self.parts.errors))
250 }
251 AsyncResourceStatus::WaitingForInput => AsyncPollDisposition::WaitingForInput,
252 _ => AsyncPollDisposition::Update(ActionUpdate::Running),
253 }
254 }
255}
256
257impl fmt::Debug for AsyncTask<'_> {
258 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
259 formatter
260 .debug_struct("AsyncTask")
261 .field("id", &"[redacted]")
262 .field("kind", &"[redacted]")
263 .field("status", &self.parts.status)
264 .field("link", &"[redacted]")
265 .field("message", &"[redacted]")
266 .field("timestamps", &"[redacted]")
267 .field("progress_steps", &self.parts.progress.len())
268 .field("errors", &self.parts.errors.len())
269 .finish()
270 }
271}
272
273#[derive(Clone, Copy)]
275pub struct AsyncEventParts<'a> {
276 pub id: AsyncResourceId<'a>,
278 pub kind: AsyncResourceText<'a>,
280 pub observed_at: AsyncResourceTimestamp<'a>,
282 pub link: Option<AsyncResourceLink<'a>>,
284 pub message: Option<AsyncResourceText<'a>>,
286}
287
288#[derive(Clone, Copy)]
290pub struct AsyncEvent<'a> {
291 parts: AsyncEventParts<'a>,
292}
293
294impl<'a> AsyncEvent<'a> {
295 #[must_use]
297 pub const fn new(parts: AsyncEventParts<'a>) -> Self {
298 Self { parts }
299 }
300
301 #[must_use]
303 pub const fn id(&self) -> AsyncResourceId<'a> {
304 self.parts.id
305 }
306
307 #[must_use]
309 pub const fn kind(&self) -> AsyncResourceText<'a> {
310 self.parts.kind
311 }
312
313 #[must_use]
315 pub const fn observed_at(&self) -> AsyncResourceTimestamp<'a> {
316 self.parts.observed_at
317 }
318
319 #[must_use]
321 pub const fn link(&self) -> Option<AsyncResourceLink<'a>> {
322 self.parts.link
323 }
324
325 #[must_use]
327 pub const fn message(&self) -> Option<AsyncResourceText<'a>> {
328 self.parts.message
329 }
330}
331
332impl fmt::Debug for AsyncEvent<'_> {
333 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
334 formatter.write_str("AsyncEvent([redacted])")
335 }
336}
337
338pub struct AsyncEventBatch<'a> {
340 events: &'a [AsyncEvent<'a>],
341}
342
343impl<'a> AsyncEventBatch<'a> {
344 pub fn new(events: &'a [AsyncEvent<'a>]) -> Result<Self, AsyncResourceValidationError> {
346 if events.len() > MAX_ASYNC_EVENTS {
347 return Err(AsyncResourceValidationError::TooManyEvents);
348 }
349 Ok(Self { events })
350 }
351
352 #[must_use]
354 pub const fn events(&self) -> &'a [AsyncEvent<'a>] {
355 self.events
356 }
357}
358
359impl fmt::Debug for AsyncEventBatch<'_> {
360 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
361 formatter
362 .debug_struct("AsyncEventBatch")
363 .field("events", &self.events.len())
364 .finish()
365 }
366}