1use anyhow::format_err;
5use std::cell::RefCell;
6use tokio::time::{Duration, Instant};
7
8thread_local! {
9 static SCRATCH: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
11}
12
13pub(crate) fn with_scratch<R>(len: usize, f: impl FnOnce(&mut [u8]) -> R) -> R {
19 SCRATCH.with(|scratch| match scratch.try_borrow_mut() {
20 Ok(mut scratch) => {
21 if scratch.len() < len {
22 scratch.resize(len, 0);
23 }
24 f(&mut scratch[..len])
25 }
26 Err(_) => f(&mut vec![0; len]),
28 })
29}
30
31pub(crate) fn format_duration(duration: Duration) -> String {
34 let secs = (duration + Duration::from_millis(500)).as_secs();
36 match (secs / 60, secs % 60) {
37 (0, secs) => format!("{secs}s"),
38 (mins, 0) => format!("{mins}m"),
39 (mins, secs) => format!("{mins}m {secs}s"),
40 }
41}
42
43pub(crate) async fn sleep_until(deadline: Option<Instant>) {
45 match deadline {
46 Some(deadline) => tokio::time::sleep_until(deadline).await,
47 None => std::future::pending().await,
48 }
49}
50
51pub(crate) fn with_stderr(error: anyhow::Error, stderr: &[u8]) -> anyhow::Error {
54 let stderr = String::from_utf8_lossy(stderr);
55 let stderr = stderr.trim();
56 if stderr.is_empty() {
57 error
58 } else {
59 format_err!("{error:#}: {stderr}")
60 }
61}