rvlib/
util.rs

1use lazy_static::lazy_static;
2use regex::Regex;
3use serde::{Deserialize, Serialize};
4use std::{cmp::Ordering, mem};
5
6pub fn wrap_if<T>(x: T, cond: bool) -> Option<T> {
7    if cond { Some(x) } else { None }
8}
9
10pub fn sort_by_vec<T, U>(sort_keys: &[U], mut v: Vec<T>) -> Vec<T>
11where
12    T: Default,
13    U: Ord,
14{
15    let mut idxs = (0..sort_keys.len()).collect::<Vec<_>>();
16    idxs.sort_unstable_by_key(|&i| &sort_keys[i]);
17    idxs.iter()
18        .map(|i| mem::take(&mut v[*i]))
19        .collect::<Vec<_>>()
20}
21
22#[allow(clippy::needless_lifetimes)]
23fn xor_mask<'a>(mask: &'a [bool], other: bool) -> impl Iterator<Item = usize> + Clone + 'a {
24    mask.iter()
25        .enumerate()
26        .filter(move |(_, is_selected)| other ^ **is_selected)
27        .map(|(i, _)| i)
28}
29
30#[allow(clippy::needless_lifetimes)]
31pub fn true_indices<'a>(mask: &'a [bool]) -> impl Iterator<Item = usize> + Clone + 'a {
32    xor_mask(mask, false)
33}
34
35pub fn natural_cmp(s1: &str, s2: &str) -> Ordering {
36    lazy_static! {
37        static ref RE: Regex = Regex::new(r"(\d+)").unwrap();
38    }
39    let mut idx = 0;
40    while idx < s1.len().min(s2.len()) {
41        let c1 = s1.chars().nth(idx).unwrap();
42        let c2 = s2.chars().nth(idx).unwrap();
43        if c1.is_ascii_digit() && c2.is_ascii_digit() {
44            let n1 = RE.captures(&s1[idx..]).unwrap()[0]
45                .parse::<usize>()
46                .unwrap();
47            let n2 = RE.captures(&s2[idx..]).unwrap()[0]
48                .parse::<usize>()
49                .unwrap();
50            if n1 != n2 {
51                return n1.cmp(&n2);
52            }
53            idx += n1.to_string().len();
54        } else {
55            if c1 != c2 {
56                return c1.cmp(&c2);
57            }
58            idx += 1;
59        }
60    }
61    s1.len().cmp(&s2.len())
62}
63pub fn version_label() -> String {
64    const VERSION: &str = env!("CARGO_PKG_VERSION");
65    const GIT_DESC: &str = env!("GIT_DESC");
66    #[allow(clippy::const_is_empty)]
67    if GIT_DESC.is_empty() {
68        format!("Version {VERSION}")
69    } else {
70        const GIT_DIRTY: &str = env!("GIT_DIRTY");
71        let is_dirty = GIT_DIRTY == "true";
72        format!(
73            "Version {}{}\n",
74            &GIT_DESC,
75            if is_dirty { " DIRTY" } else { "" }
76        )
77    }
78}
79
80#[macro_export]
81macro_rules! measure_time {
82    ($name:expr, $block:expr) => {{
83        // let start = std::time::Instant::now();
84        let result = $block;
85        // tracing::warn!("{} took {} millis", $name, start.elapsed().as_millis());
86        result
87    }};
88}
89
90#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
91pub enum Visibility {
92    All,
93    None,
94    // contains index of label that is to be shown exclusively
95    Only(usize),
96}
97#[test]
98fn test_natural_sort() {
99    assert_eq!(natural_cmp("s10", "s2"), Ordering::Greater);
100    assert_eq!(natural_cmp("10s", "s2"), Ordering::Less);
101    assert_eq!(natural_cmp("10", "2"), Ordering::Greater);
102    assert_eq!(natural_cmp("10.0", "10.0"), Ordering::Equal);
103    assert_eq!(natural_cmp("20.0", "10.0"), Ordering::Greater);
104    assert_eq!(
105        natural_cmp("a lot of text 20.0 .", "a lot of text 100.0"),
106        Ordering::Less
107    );
108    assert_eq!(
109        natural_cmp("a lot of 7text 20.0 .", "a lot of 3text 100.0"),
110        Ordering::Greater
111    );
112}
113
114pub struct Defer<F: FnMut()> {
115    pub func: F,
116}
117impl<F: FnMut()> Drop for Defer<F> {
118    fn drop(&mut self) {
119        (self.func)();
120    }
121}
122#[macro_export]
123macro_rules! defer {
124    ($f:expr) => {
125        let _dfr = $crate::Defer { func: $f };
126    };
127}
128#[macro_export]
129macro_rules! time_scope {
130    ($name:expr) => {
131        let now = std::time::Instant::now();
132        #[cfg(feature = "print_timings")]
133        let f = || eprintln!("{} {}", $name, now.elapsed().as_micros());
134        #[cfg(not(feature = "print_timings"))]
135        let f = || ();
136        $crate::defer!(f);
137    };
138}