1use rand::prelude::*;
2use std::cmp::Ordering;
3
4#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
5pub enum Bias {
6 Left,
7 Right,
8}
9
10impl PartialOrd for Bias {
11 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
12 Some(self.cmp(other))
13 }
14}
15
16impl Ord for Bias {
17 fn cmp(&self, other: &Self) -> Ordering {
18 match (self, other) {
19 (Self::Left, Self::Left) => Ordering::Equal,
20 (Self::Left, Self::Right) => Ordering::Less,
21 (Self::Right, Self::Right) => Ordering::Equal,
22 (Self::Right, Self::Left) => Ordering::Greater,
23 }
24 }
25}
26
27pub fn post_inc(value: &mut usize) -> usize {
28 let prev = *value;
29 *value += 1;
30 prev
31}
32
33/// Extend a sorted vector with a sorted sequence of items, maintaining the vector's sort order and
34/// enforcing a maximum length. Sort the items according to the given callback. Before calling this,
35/// both `vec` and `new_items` should already be sorted according to the `cmp` comparator.
36pub fn extend_sorted<T, I, F>(vec: &mut Vec<T>, new_items: I, limit: usize, mut cmp: F)
37where
38 I: IntoIterator<Item = T>,
39 F: FnMut(&T, &T) -> Ordering,
40{
41 let mut start_index = 0;
42 for new_item in new_items {
43 if let Err(i) = vec[start_index..].binary_search_by(|m| cmp(m, &new_item)) {
44 let index = start_index + i;
45 if vec.len() < limit {
46 vec.insert(index, new_item);
47 } else if index < vec.len() {
48 vec.pop();
49 vec.insert(index, new_item);
50 }
51 start_index = index;
52 }
53 }
54}
55
56pub struct RandomCharIter<T: Rng>(T);
57
58impl<T: Rng> RandomCharIter<T> {
59 #[cfg(test)]
60 pub fn new(rng: T) -> Self {
61 Self(rng)
62 }
63}
64
65impl<T: Rng> Iterator for RandomCharIter<T> {
66 type Item = char;
67
68 fn next(&mut self) -> Option<Self::Item> {
69 if self.0.gen_bool(1.0 / 5.0) {
70 Some('\n')
71 }
72 // two-byte greek letters
73 else if self.0.gen_bool(1.0 / 8.0) {
74 Some(std::char::from_u32(self.0.gen_range(('α' as u32)..('ω' as u32 + 1))).unwrap())
75 }
76 // three-byte characters
77 else if self.0.gen_bool(1.0 / 10.0) {
78 ['✋', '✅', '❌', '❎', '⭐'].choose(&mut self.0).cloned()
79 }
80 // four-byte characters
81 else if self.0.gen_bool(1.0 / 12.0) {
82 ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.0).cloned()
83 }
84 // ascii letters
85 else {
86 Some(self.0.gen_range(b'a'..b'z' + 1).into())
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_extend_sorted() {
97 let mut vec = vec![];
98
99 extend_sorted(&mut vec, vec![21, 17, 13, 8, 1, 0], 5, |a, b| b.cmp(a));
100 assert_eq!(vec, &[21, 17, 13, 8, 1]);
101
102 extend_sorted(&mut vec, vec![101, 19, 17, 8, 2], 8, |a, b| b.cmp(a));
103 assert_eq!(vec, &[101, 21, 19, 17, 13, 8, 2, 1]);
104
105 extend_sorted(&mut vec, vec![1000, 19, 17, 9, 5], 8, |a, b| b.cmp(a));
106 assert_eq!(vec, &[1000, 101, 21, 19, 17, 13, 9, 8]);
107 }
108}
109
110// Allow surf Results to accept context like other Results do when
111// using anyhow.
112pub trait SurfResultExt {
113 fn context<C>(self, cx: C) -> Self
114 where
115 C: std::fmt::Display + Send + Sync + 'static;
116
117 fn with_context<C, F>(self, f: F) -> Self
118 where
119 C: std::fmt::Display + Send + Sync + 'static,
120 F: FnOnce() -> C;
121}
122
123impl<T> SurfResultExt for surf::Result<T> {
124 fn context<C>(self, cx: C) -> Self
125 where
126 C: std::fmt::Display + Send + Sync + 'static,
127 {
128 self.map_err(|e| surf::Error::new(e.status(), e.into_inner().context(cx)))
129 }
130
131 fn with_context<C, F>(self, f: F) -> Self
132 where
133 C: std::fmt::Display + Send + Sync + 'static,
134 F: FnOnce() -> C,
135 {
136 self.map_err(|e| surf::Error::new(e.status(), e.into_inner().context(f())))
137 }
138}