Skip to main content

repo_quest/
utils.rs

1use eyre::{Context, Result};
2use std::{ops::Range, time::Duration};
3
4pub fn replace_many_ranges(
5  s: &mut String,
6  ranges: impl IntoIterator<Item = (Range<usize>, impl AsRef<str>)>,
7) {
8  let ranges = ranges.into_iter().collect::<Vec<_>>();
9  if !ranges.is_empty() {
10    debug_assert!((0..ranges.len() - 1).all(|i| ranges[i].0.end <= ranges[i + 1].0.start));
11    for (range, content) in ranges.into_iter().rev() {
12      s.replace_range(range, content.as_ref());
13    }
14  }
15}
16
17pub enum RetryError {
18  Wait,
19  Err(eyre::Error),
20}
21
22pub async fn retry_with_timeout<T, Fut>(f: impl Fn() -> Fut) -> eyre::Result<T>
23where
24  Fut: Future<Output = Result<T, RetryError>>,
25{
26  const RETRY_INTERVAL: u64 = 500;
27  const RETRY_TIMEOUT: u64 = 5000;
28
29  let strategy = tokio_retry::strategy::FixedInterval::from_millis(RETRY_INTERVAL);
30  let retry_fut = tokio_retry::RetryIf::spawn(strategy, f, |e: &_| matches!(e, RetryError::Wait));
31  let res = tokio::time::timeout(Duration::from_millis(RETRY_TIMEOUT), retry_fut)
32    .await
33    .context("Operation timed out")?;
34  res.map_err(|e| match e {
35    RetryError::Err(e) => e,
36    RetryError::Wait => unreachable!(),
37  })
38}
39
40#[test]
41fn test_replace_many_ranges() {
42  let mut s = "Hello world".to_string();
43  replace_many_ranges(&mut s, [(0..1, "Y"), (6..11, "BB")]);
44  assert_eq!(s, "Yello BB");
45}