lib.rs

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