1use std::any::Any;
8use std::marker::PhantomData;
9use std::time::{Duration, Instant};
10
11use tokio::sync::oneshot;
12
13use crate::error::SteamError;
14use crate::steam::steam_client::callback_mgr::callback_msg::CallbackMsg;
15
16const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
18
19const HEARTBEAT_TIMEOUT_EXTENSION: Duration = Duration::from_secs(10);
21
22#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
24pub struct JobID(u64);
25
26impl JobID {
27 pub const INVALID: JobID = JobID(u64::MAX);
29
30 pub fn new() -> JobID {
32 JobID(u64::MAX)
33 }
34
35 pub fn from_value(value: u64) -> JobID {
37 JobID(value)
38 }
39
40 pub fn value(&self) -> u64 {
42 self.0
43 }
44
45 pub fn set_value(&mut self, value: u64) {
47 self.0 = value;
48 }
49
50 pub fn sequential_count(&self) -> u32 {
52 ((self.0 >> 0) & 0xF_FFFF) as u32
53 }
54
55 pub fn set_sequential_count(&mut self, value: u32) {
57 let mask = 0xF_FFFF_u64;
58 self.0 = (self.0 & !mask) | (value as u64 & mask);
59 }
60
61 pub fn start_time(&self) -> u64 {
63 (self.0 >> 20) & 0x3_FFFF_FFFF
64 }
65
66 pub fn set_start_time(&mut self, value: u64) {
68 let mask = 0x3_FFFF_FFFF_u64 << 20;
69 self.0 = (self.0 & !mask) | ((value & 0x3_FFFF_FFFF) << 20);
70 }
71
72 pub fn process_id(&self) -> u32 {
74 ((self.0 >> 50) & 0xF) as u32
75 }
76
77 pub fn set_process_id(&mut self, value: u32) {
79 let mask = 0xF_u64 << 50;
80 self.0 = (self.0 & !mask) | ((value as u64 & 0xF) << 50);
81 }
82
83 pub fn box_id(&self) -> u32 {
85 ((self.0 >> 54) & 0x3_FF) as u32
86 }
87
88 pub fn set_box_id(&mut self, value: u32) {
90 let mask = 0x3_FF_u64 << 54;
91 self.0 = (self.0 & !mask) | ((value as u64 & 0x3_FF) << 54);
92 }
93}
94
95impl std::fmt::Display for JobID {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 write!(f, "{}", self.value())
98 }
99}
100
101impl std::fmt::Debug for JobID {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 std::fmt::Display::fmt(self, f)
104 }
105}
106
107impl From<JobID> for u64 {
108 fn from(job_id: JobID) -> u64 {
109 job_id.value()
110 }
111}
112
113impl From<u64> for JobID {
114 fn from(value: u64) -> JobID {
115 JobID::from_value(value)
116 }
117}
118
119type JobResult = Result<Box<dyn CallbackMsg>, SteamError>;
121
122#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
124pub struct AnyCallback;
125
126pub struct AsyncJob<T = AnyCallback> {
128 job_id: JobID,
129 timeout: Duration,
130 job_start: Instant,
131 sender: Option<oneshot::Sender<JobResult>>,
132 receiver: JobReceiver,
133 _marker: PhantomData<fn() -> T>,
134}
135
136impl<T> AsyncJob<T> {
137 pub fn new(job_id: JobID) -> AsyncJob<T> {
140 let (sender, receiver) = oneshot::channel();
141 AsyncJob {
142 job_id,
143 timeout: DEFAULT_TIMEOUT,
144 job_start: Instant::now(),
145 sender: Some(sender),
146 receiver: JobReceiver { receiver },
147 _marker: PhantomData,
148 }
149 }
150
151 pub fn job_id(&self) -> JobID {
153 self.job_id
154 }
155
156 pub fn timeout(&self) -> Duration {
158 self.timeout
159 }
160
161 pub fn set_timeout(&mut self, timeout: Duration) {
163 self.timeout = timeout;
164 }
165
166 pub fn is_timedout(&self) -> bool {
168 self.job_start.elapsed() >= self.timeout
169 }
170
171 pub fn heartbeat(&mut self) {
173 self.timeout += HEARTBEAT_TIMEOUT_EXTENSION;
174 }
175
176 pub fn add_result(&mut self, callback: Box<dyn CallbackMsg>) -> bool {
179 if let Some(sender) = self.sender.take() {
180 let _ = sender.send(Ok(callback));
181 }
182 true
183 }
184
185 pub fn set_failed(&mut self, due_to_remote_failure: bool) {
188 if let Some(sender) = self.sender.take() {
189 let error = if due_to_remote_failure {
190 SteamError::JobFailed
191 } else {
192 SteamError::Timeout(self.timeout)
193 };
194 let _ = sender.send(Err(error));
195 }
196 }
197
198 pub fn take_sender(&mut self) -> Option<oneshot::Sender<JobResult>> {
201 self.sender.take()
202 }
203
204 pub fn into_receiver(self) -> JobReceiver {
206 self.receiver
207 }
208}
209
210impl<T> From<&AsyncJob<T>> for JobID {
211 fn from(job: &AsyncJob<T>) -> JobID {
212 job.job_id()
213 }
214}
215
216impl<T> std::fmt::Debug for AsyncJob<T> {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 f.debug_struct("AsyncJob")
219 .field("job_id", &self.job_id)
220 .field("timeout", &self.timeout)
221 .finish()
222 }
223}
224
225impl<T: CallbackMsg> AsyncJob<T> {
226 pub fn to_task(self) -> TypedJobReceiver<T> {
228 TypedJobReceiver {
229 receiver: self.receiver,
230 _marker: PhantomData,
231 }
232 }
233
234 pub async fn wait(self) -> Result<T, SteamError> {
236 self.to_task().wait().await
237 }
238}
239
240pub struct JobReceiver {
242 receiver: oneshot::Receiver<JobResult>,
243}
244
245impl JobReceiver {
246 pub async fn wait(self) -> Result<Box<dyn CallbackMsg>, SteamError> {
248 match self.receiver.await {
249 Ok(result) => result,
250 Err(_) => Err(SteamError::Cancelled),
251 }
252 }
253}
254
255impl std::fmt::Debug for JobReceiver {
256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 f.write_str("JobReceiver")
258 }
259}
260
261pub struct TypedJobReceiver<T: CallbackMsg> {
263 receiver: JobReceiver,
264 _marker: PhantomData<fn() -> T>,
265}
266
267impl<T: CallbackMsg> TypedJobReceiver<T> {
268 pub async fn wait(self) -> Result<T, SteamError> {
270 let message = self.receiver.wait().await?;
271
272 let any: Box<dyn Any> = message;
274 match any.downcast::<T>() {
275 Ok(typed) => Ok(*typed),
276 Err(_) => Err(SteamError::Malformed(
277 "callback message does not match the expected type".to_string(),
278 )),
279 }
280 }
281}
282
283impl<T: CallbackMsg> std::fmt::Debug for TypedJobReceiver<T> {
284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285 f.write_str("TypedJobReceiver")
286 }
287}
288
289#[derive(Clone, Debug, PartialEq, Eq)]
291pub struct ResultSet<T> {
292 pub complete: bool,
294 pub failed: bool,
296 pub results: Option<Vec<T>>,
298}
299
300pub struct AsyncJobMultiple<T: CallbackMsg> {
302 job_id: JobID,
303 timeout: Duration,
304 job_start: Instant,
305 sender: Option<oneshot::Sender<Result<ResultSet<T>, SteamError>>>,
306 receiver: MultiJobReceiver<T>,
307 finish_condition: Box<dyn FnMut(&T) -> bool + Send + Sync>,
308 results: Vec<T>,
309 _marker: PhantomData<fn() -> T>,
310}
311
312impl<T: CallbackMsg> AsyncJobMultiple<T> {
313 pub fn new(
316 job_id: JobID,
317 finish_condition: impl FnMut(&T) -> bool + Send + Sync + 'static,
318 ) -> AsyncJobMultiple<T> {
319 let (sender, receiver) = oneshot::channel();
320 AsyncJobMultiple {
321 job_id,
322 timeout: DEFAULT_TIMEOUT,
323 job_start: Instant::now(),
324 sender: Some(sender),
325 receiver: MultiJobReceiver {
326 receiver,
327 _marker: PhantomData,
328 },
329 finish_condition: Box::new(finish_condition),
330 results: Vec::new(),
331 _marker: PhantomData,
332 }
333 }
334
335 pub fn job_id(&self) -> JobID {
337 self.job_id
338 }
339
340 pub fn timeout(&self) -> Duration {
342 self.timeout
343 }
344
345 pub fn set_timeout(&mut self, timeout: Duration) {
347 self.timeout = timeout;
348 }
349
350 pub fn is_timedout(&self) -> bool {
352 self.job_start.elapsed() >= self.timeout
353 }
354
355 pub fn heartbeat(&mut self) {
357 self.timeout += HEARTBEAT_TIMEOUT_EXTENSION;
358 }
359
360 pub fn results(&self) -> &[T] {
362 &self.results
363 }
364
365 pub fn add_result(&mut self, callback: Box<dyn CallbackMsg>) -> bool {
367 let Some(callback) = downcast_callback::<T>(callback) else {
368 return false;
369 };
370
371 let is_complete = (self.finish_condition)(&callback);
372
373 self.results.push(callback);
375
376 if is_complete {
377 let result_set = ResultSet {
381 complete: true,
382 failed: false,
383 results: Some(std::mem::take(&mut self.results)),
384 };
385
386 if let Some(sender) = self.sender.take() {
387 let _ = sender.send(Ok(result_set));
388 }
389
390 return true;
391 }
392
393 self.heartbeat();
397
398 false
399 }
400
401 pub fn set_failed(&mut self, due_to_remote_failure: bool) {
404 if self.results.is_empty() {
408 let error = if due_to_remote_failure {
410 SteamError::JobFailed
411 } else {
412 SteamError::Timeout(self.timeout)
413 };
414
415 if let Some(sender) = self.sender.take() {
416 let _ = sender.send(Err(error));
417 }
418 } else {
419 let result_set = ResultSet {
423 complete: false,
425 failed: due_to_remote_failure,
427 results: Some(std::mem::take(&mut self.results)),
428 };
429
430 if let Some(sender) = self.sender.take() {
431 let _ = sender.send(Ok(result_set));
432 }
433 }
434 }
435
436 pub fn take_sender(&mut self) -> Option<oneshot::Sender<Result<ResultSet<T>, SteamError>>> {
438 self.sender.take()
439 }
440
441 pub fn to_task(self) -> MultiJobReceiver<T> {
443 self.receiver
444 }
445
446 pub async fn wait(self) -> Result<ResultSet<T>, SteamError> {
448 self.to_task().wait().await
449 }
450}
451
452impl<T: CallbackMsg> std::fmt::Debug for AsyncJobMultiple<T> {
453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454 f.debug_struct("AsyncJobMultiple")
455 .field("job_id", &self.job_id)
456 .field("timeout", &self.timeout)
457 .field("results", &self.results.len())
458 .finish()
459 }
460}
461
462pub struct MultiJobReceiver<T: CallbackMsg> {
464 receiver: oneshot::Receiver<Result<ResultSet<T>, SteamError>>,
465 _marker: PhantomData<fn() -> T>,
466}
467
468impl<T: CallbackMsg> MultiJobReceiver<T> {
469 pub async fn wait(self) -> Result<ResultSet<T>, SteamError> {
471 match self.receiver.await {
472 Ok(result) => result,
473 Err(_) => Err(SteamError::Cancelled),
474 }
475 }
476}
477
478impl<T: CallbackMsg> std::fmt::Debug for MultiJobReceiver<T> {
479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
480 f.write_str("MultiJobReceiver")
481 }
482}
483
484pub type JobState = AsyncJob;
486
487pub type JobHandle<T> = AsyncJob<T>;
489
490pub type TypedJob<T> = TypedJobReceiver<T>;
492
493pub type JobGroup<T> = AsyncJobMultiple<T>;
495
496pub type MultiJob<T> = MultiJobReceiver<T>;
498
499fn downcast_callback<T: CallbackMsg>(callback: Box<dyn CallbackMsg>) -> Option<T> {
501 let any: Box<dyn Any> = callback;
503 any.downcast::<T>().ok().map(|typed| *typed)
504}