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