util.rs

  1use futures::Future;
  2use rand::prelude::*;
  3use std::cmp::Ordering;
  4
  5#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
  6pub enum Bias {
  7    Left,
  8    Right,
  9}
 10
 11impl PartialOrd for Bias {
 12    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 13        Some(self.cmp(other))
 14    }
 15}
 16
 17impl Ord for Bias {
 18    fn cmp(&self, other: &Self) -> Ordering {
 19        match (self, other) {
 20            (Self::Left, Self::Left) => Ordering::Equal,
 21            (Self::Left, Self::Right) => Ordering::Less,
 22            (Self::Right, Self::Right) => Ordering::Equal,
 23            (Self::Right, Self::Left) => Ordering::Greater,
 24        }
 25    }
 26}
 27
 28pub fn post_inc(value: &mut usize) -> usize {
 29    let prev = *value;
 30    *value += 1;
 31    prev
 32}
 33
 34/// Extend a sorted vector with a sorted sequence of items, maintaining the vector's sort order and
 35/// enforcing a maximum length. Sort the items according to the given callback. Before calling this,
 36/// both `vec` and `new_items` should already be sorted according to the `cmp` comparator.
 37pub fn extend_sorted<T, I, F>(vec: &mut Vec<T>, new_items: I, limit: usize, mut cmp: F)
 38where
 39    I: IntoIterator<Item = T>,
 40    F: FnMut(&T, &T) -> Ordering,
 41{
 42    let mut start_index = 0;
 43    for new_item in new_items {
 44        if let Err(i) = vec[start_index..].binary_search_by(|m| cmp(m, &new_item)) {
 45            let index = start_index + i;
 46            if vec.len() < limit {
 47                vec.insert(index, new_item);
 48            } else if index < vec.len() {
 49                vec.pop();
 50                vec.insert(index, new_item);
 51            }
 52            start_index = index;
 53        }
 54    }
 55}
 56
 57pub struct RandomCharIter<T: Rng>(T);
 58
 59impl<T: Rng> RandomCharIter<T> {
 60    #[cfg(test)]
 61    pub fn new(rng: T) -> Self {
 62        Self(rng)
 63    }
 64}
 65
 66impl<T: Rng> Iterator for RandomCharIter<T> {
 67    type Item = char;
 68
 69    fn next(&mut self) -> Option<Self::Item> {
 70        match self.0.gen_range(0..100) {
 71            // whitespace
 72            0..=19 => [' ', '\n', '\t'].choose(&mut self.0).copied(),
 73            // two-byte greek letters
 74            20..=32 => char::from_u32(self.0.gen_range(('α' as u32)..('ω' as u32 + 1))),
 75            // three-byte characters
 76            33..=45 => ['✋', '✅', '❌', '❎', '⭐'].choose(&mut self.0).copied(),
 77            // four-byte characters
 78            46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.0).copied(),
 79            // ascii letters
 80            _ => Some(self.0.gen_range(b'a'..b'z' + 1).into()),
 81        }
 82    }
 83}
 84
 85pub async fn log_async_errors<F>(f: F)
 86where
 87    F: Future<Output = anyhow::Result<()>>,
 88{
 89    if let Err(error) = f.await {
 90        log::error!("{}", error)
 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}