lib.rs

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