util.rs

  1use std::sync::atomic::AtomicUsize;
  2use std::sync::atomic::Ordering::SeqCst;
  3#[cfg(any(test, feature = "test-support"))]
  4use std::time::Duration;
  5
  6#[cfg(any(test, feature = "test-support"))]
  7use futures::Future;
  8
  9#[cfg(any(test, feature = "test-support"))]
 10use smol::future::FutureExt;
 11
 12pub use util::*;
 13
 14/// A helper trait for building complex objects with imperative conditionals in a fluent style.
 15pub trait FluentBuilder {
 16    /// Imperatively modify self with the given closure.
 17    fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
 18    where
 19        Self: Sized,
 20    {
 21        f(self)
 22    }
 23
 24    /// Conditionally modify self with the given closure.
 25    fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
 26    where
 27        Self: Sized,
 28    {
 29        self.map(|this| if condition { then(this) } else { this })
 30    }
 31
 32    /// Conditionally modify self with the given closure.
 33    fn when_else(
 34        self,
 35        condition: bool,
 36        then: impl FnOnce(Self) -> Self,
 37        else_fn: impl FnOnce(Self) -> Self,
 38    ) -> Self
 39    where
 40        Self: Sized,
 41    {
 42        self.map(|this| if condition { then(this) } else { else_fn(this) })
 43    }
 44
 45    /// Conditionally unwrap and modify self with the given closure, if the given option is Some.
 46    fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
 47    where
 48        Self: Sized,
 49    {
 50        self.map(|this| {
 51            if let Some(value) = option {
 52                then(this, value)
 53            } else {
 54                this
 55            }
 56        })
 57    }
 58    /// Conditionally unwrap and modify self with the given closure, if the given option is None.
 59    fn when_none<T>(self, option: &Option<T>, then: impl FnOnce(Self) -> Self) -> Self
 60    where
 61        Self: Sized,
 62    {
 63        self.map(|this| {
 64            if let Some(_) = option {
 65                this
 66            } else {
 67                then(this)
 68            }
 69        })
 70    }
 71}
 72
 73#[cfg(any(test, feature = "test-support"))]
 74pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
 75where
 76    F: Future<Output = T>,
 77{
 78    let timer = async {
 79        smol::Timer::after(timeout).await;
 80        Err(())
 81    };
 82    let future = async move { Ok(f.await) };
 83    timer.race(future).await
 84}
 85
 86#[cfg(any(test, feature = "test-support"))]
 87pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);
 88
 89#[cfg(any(test, feature = "test-support"))]
 90impl std::fmt::Debug for CwdBacktrace<'_> {
 91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 92        use backtrace::{BacktraceFmt, BytesOrWideString};
 93
 94        let cwd = std::env::current_dir().unwrap();
 95        let cwd = cwd.parent().unwrap();
 96        let mut print_path = |fmt: &mut std::fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
 97            std::fmt::Display::fmt(&path, fmt)
 98        };
 99        let mut fmt = BacktraceFmt::new(f, backtrace::PrintFmt::Full, &mut print_path);
100        for frame in self.0.frames() {
101            let mut formatted_frame = fmt.frame();
102            if frame
103                .symbols()
104                .iter()
105                .any(|s| s.filename().map_or(false, |f| f.starts_with(cwd)))
106            {
107                formatted_frame.backtrace_frame(frame)?;
108            }
109        }
110        fmt.finish()
111    }
112}
113
114/// Increment the given atomic counter if it is not zero.
115/// Return the new value of the counter.
116pub(crate) fn atomic_incr_if_not_zero(counter: &AtomicUsize) -> usize {
117    let mut loaded = counter.load(SeqCst);
118    loop {
119        if loaded == 0 {
120            return 0;
121        }
122        match counter.compare_exchange_weak(loaded, loaded + 1, SeqCst, SeqCst) {
123            Ok(x) => return x + 1,
124            Err(actual) => loaded = actual,
125        }
126    }
127}