1use std::future::Future;
2
3use color_eyre::eyre::eyre;
4
5pub struct NamedTask {
8 name: &'static str,
9 handle: tokio::task::JoinHandle<crate::Result<()>>,
10}
11
12impl NamedTask {
13 pub fn spawn<F>(name: &'static str, future: F) -> Self
15 where
16 F: Future<Output = crate::Result<()>> + Send + 'static,
17 {
18 Self {
19 name,
20 handle: tokio::spawn(future),
21 }
22 }
23
24 pub fn name(&self) -> &'static str {
26 self.name
27 }
28}
29
30pub async fn wait_for_first_task(
36 tasks: Vec<NamedTask>,
37) -> (
38 &'static str,
39 Result<crate::Result<()>, tokio::task::JoinError>,
40) {
41 let (handles, names): (Vec<_>, Vec<_>) = tasks.into_iter().map(|t| (t.handle, t.name)).unzip();
42
43 let (result, index, _remaining) = futures::future::select_all(handles).await;
44 (names[index], result)
45}
46
47pub async fn wait_for_first_error(tasks: Vec<NamedTask>) -> crate::Result<()> {
53 if tasks.is_empty() {
54 return Ok(());
55 }
56
57 let (name, result) = wait_for_first_task(tasks).await;
58
59 match result {
60 Ok(Ok(())) => {
61 tracing::error!(task = name, "Task exited unexpectedly");
62 Err(eyre!("Task '{}' exited unexpectedly", name))
63 }
64 Ok(Err(e)) => {
65 tracing::error!(task = name, error = ?e, "Task failed with error");
66 Err(e)
67 }
68 Err(join_error) => {
69 tracing::error!(task = name, error = ?join_error, "Task panicked");
70 Err(eyre!("Task '{}' panicked: {}", name, join_error))
71 }
72 }
73}