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}
44
45impl<T> ResultExt for anyhow::Result<T> {
46 type Ok = T;
47
48 fn log_err(self) -> Option<T> {
49 match self {
50 Ok(value) => Some(value),
51 Err(error) => {
52 log::error!("{:?}", error);
53 None
54 }
55 }
56 }
57}
58
59pub trait TryFutureExt {
60 fn log_err(self) -> LogErrorFuture<Self>
61 where
62 Self: Sized;
63}
64
65impl<F, T> TryFutureExt for F
66where
67 F: Future<Output = anyhow::Result<T>>,
68{
69 fn log_err(self) -> LogErrorFuture<Self>
70 where
71 Self: Sized,
72 {
73 LogErrorFuture(self)
74 }
75}
76
77pub struct LogErrorFuture<F>(F);
78
79impl<F, T> Future for LogErrorFuture<F>
80where
81 F: Future<Output = anyhow::Result<T>>,
82{
83 type Output = Option<T>;
84
85 fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
86 let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) };
87 match inner.poll(cx) {
88 Poll::Ready(output) => Poll::Ready(output.log_err()),
89 Poll::Pending => Poll::Pending,
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_extend_sorted() {
100 let mut vec = vec![];
101
102 extend_sorted(&mut vec, vec![21, 17, 13, 8, 1, 0], 5, |a, b| b.cmp(a));
103 assert_eq!(vec, &[21, 17, 13, 8, 1]);
104
105 extend_sorted(&mut vec, vec![101, 19, 17, 8, 2], 8, |a, b| b.cmp(a));
106 assert_eq!(vec, &[101, 21, 19, 17, 13, 8, 2, 1]);
107
108 extend_sorted(&mut vec, vec![1000, 19, 17, 9, 5], 8, |a, b| b.cmp(a));
109 assert_eq!(vec, &[1000, 101, 21, 19, 17, 13, 9, 8]);
110 }
111}
112
113// Allow surf Results to accept context like other Results do when
114// using anyhow.
115pub trait SurfResultExt {
116 fn context<C>(self, cx: C) -> Self
117 where
118 C: std::fmt::Display + Send + Sync + 'static;
119
120 fn with_context<C, F>(self, f: F) -> Self
121 where
122 C: std::fmt::Display + Send + Sync + 'static,
123 F: FnOnce() -> C;
124}
125
126impl<T> SurfResultExt for surf::Result<T> {
127 fn context<C>(self, cx: C) -> Self
128 where
129 C: std::fmt::Display + Send + Sync + 'static,
130 {
131 self.map_err(|e| surf::Error::new(e.status(), e.into_inner().context(cx)))
132 }
133
134 fn with_context<C, F>(self, f: F) -> Self
135 where
136 C: std::fmt::Display + Send + Sync + 'static,
137 F: FnOnce() -> C,
138 {
139 self.map_err(|e| surf::Error::new(e.status(), e.into_inner().context(f())))
140 }
141}