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