rvlib/
util.rs

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