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}