1#[cfg(feature = "test-support")]
2pub mod test;
3
4use futures::Future;
5use std::{
6 cmp::Ordering,
7 pin::Pin,
8 task::{Context, Poll},
9};
10
11pub fn post_inc(value: &mut usize) -> usize {
12 let prev = *value;
13 *value += 1;
14 prev
15}
16
17/// Extend a sorted vector with a sorted sequence of items, maintaining the vector's sort order and
18/// enforcing a maximum length. Sort the items according to the given callback. Before calling this,
19/// both `vec` and `new_items` should already be sorted according to the `cmp` comparator.
20pub fn extend_sorted<T, I, F>(vec: &mut Vec<T>, new_items: I, limit: usize, mut cmp: F)
21where
22 I: IntoIterator<Item = T>,
23 F: FnMut(&T, &T) -> Ordering,
24{
25 let mut start_index = 0;
26 for new_item in new_items {
27 if let Err(i) = vec[start_index..].binary_search_by(|m| cmp(m, &new_item)) {
28 let index = start_index + i;
29 if vec.len() < limit {
30 vec.insert(index, new_item);
31 } else if index < vec.len() {
32 vec.pop();
33 vec.insert(index, new_item);
34 }
35 start_index = index;
36 }
37 }
38}
39pub trait ResultExt {
40 type Ok;
41
42 fn log_err(self) -> Option<Self::Ok>;
43 fn warn_on_err(self) -> Option<Self::Ok>;
44}
45
46impl<T> ResultExt for anyhow::Result<T> {
47 type Ok = T;
48
49 fn log_err(self) -> Option<T> {
50 match self {
51 Ok(value) => Some(value),
52 Err(error) => {
53 log::error!("{:?}", error);
54 None
55 }
56 }
57 }
58
59 fn warn_on_err(self) -> Option<T> {
60 match self {
61 Ok(value) => Some(value),
62 Err(error) => {
63 log::warn!("{:?}", error);
64 None
65 }
66 }
67 }
68}
69
70pub trait TryFutureExt {
71 fn log_err(self) -> LogErrorFuture<Self>
72 where
73 Self: Sized;
74 fn warn_on_err(self) -> LogErrorFuture<Self>
75 where
76 Self: Sized;
77}
78
79impl<F, T> TryFutureExt for F
80where
81 F: Future<Output = anyhow::Result<T>>,
82{
83 fn log_err(self) -> LogErrorFuture<Self>
84 where
85 Self: Sized,
86 {
87 LogErrorFuture(self, log::Level::Error)
88 }
89
90 fn warn_on_err(self) -> LogErrorFuture<Self>
91 where
92 Self: Sized,
93 {
94 LogErrorFuture(self, log::Level::Warn)
95 }
96}
97
98pub struct LogErrorFuture<F>(F, log::Level);
99
100impl<F, T> Future for LogErrorFuture<F>
101where
102 F: Future<Output = anyhow::Result<T>>,
103{
104 type Output = Option<T>;
105
106 fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
107 let level = self.1;
108 let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) };
109 match inner.poll(cx) {
110 Poll::Ready(output) => Poll::Ready(match output {
111 Ok(output) => Some(output),
112 Err(error) => {
113 log::log!(level, "{:?}", error);
114 None
115 }
116 }),
117 Poll::Pending => Poll::Pending,
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_extend_sorted() {
128 let mut vec = vec![];
129
130 extend_sorted(&mut vec, vec![21, 17, 13, 8, 1, 0], 5, |a, b| b.cmp(a));
131 assert_eq!(vec, &[21, 17, 13, 8, 1]);
132
133 extend_sorted(&mut vec, vec![101, 19, 17, 8, 2], 8, |a, b| b.cmp(a));
134 assert_eq!(vec, &[101, 21, 19, 17, 13, 8, 2, 1]);
135
136 extend_sorted(&mut vec, vec![1000, 19, 17, 9, 5], 8, |a, b| b.cmp(a));
137 assert_eq!(vec, &[1000, 101, 21, 19, 17, 13, 9, 8]);
138 }
139}
140
141// Allow surf Results to accept context like other Results do when
142// using anyhow.
143pub trait SurfResultExt {
144 fn context<C>(self, cx: C) -> Self
145 where
146 C: std::fmt::Display + Send + Sync + 'static;
147
148 fn with_context<C, F>(self, f: F) -> Self
149 where
150 C: std::fmt::Display + Send + Sync + 'static,
151 F: FnOnce() -> C;
152}
153
154impl<T> SurfResultExt for surf::Result<T> {
155 fn context<C>(self, cx: C) -> Self
156 where
157 C: std::fmt::Display + Send + Sync + 'static,
158 {
159 self.map_err(|e| surf::Error::new(e.status(), e.into_inner().context(cx)))
160 }
161
162 fn with_context<C, F>(self, f: F) -> Self
163 where
164 C: std::fmt::Display + Send + Sync + 'static,
165 F: FnOnce() -> C,
166 {
167 self.map_err(|e| surf::Error::new(e.status(), e.into_inner().context(f())))
168 }
169}