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#[macro_export]
75macro_rules! measure_time {
76    ($name:expr, $block:expr) => {{
77        // let start = std::time::Instant::now();
78        let result = $block;
79        // tracing::warn!("{} took {} millis", $name, start.elapsed().as_millis());
80        result
81    }};
82}
83
84#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
85pub enum Visibility {
86    All,
87    None,
88    // contains index of label that is to be shown exclusively
89    Only(usize),
90}
91#[test]
92fn test_natural_sort() {
93    assert_eq!(natural_cmp("s10", "s2"), Ordering::Greater);
94    assert_eq!(natural_cmp("10s", "s2"), Ordering::Less);
95    assert_eq!(natural_cmp("10", "2"), Ordering::Greater);
96    assert_eq!(natural_cmp("10.0", "10.0"), Ordering::Equal);
97    assert_eq!(natural_cmp("20.0", "10.0"), Ordering::Greater);
98    assert_eq!(
99        natural_cmp("a lot of text 20.0 .", "a lot of text 100.0"),
100        Ordering::Less
101    );
102    assert_eq!(
103        natural_cmp("a lot of 7text 20.0 .", "a lot of 3text 100.0"),
104        Ordering::Greater
105    );
106}
107
108pub struct Defer<F: FnMut()> {
109    pub func: F,
110}
111impl<F: FnMut()> Drop for Defer<F> {
112    fn drop(&mut self) {
113        (self.func)();
114    }
115}
116#[macro_export]
117macro_rules! defer {
118    ($f:expr) => {
119        let _dfr = $crate::Defer { func: $f };
120    };
121}
122#[macro_export]
123macro_rules! time_scope {
124    ($name:expr) => {
125        let now = std::time::Instant::now();
126        #[cfg(feature = "print_timings")]
127        let f = || eprintln!("{} {}", $name, now.elapsed().as_micros());
128        #[cfg(not(feature = "print_timings"))]
129        let f = || ();
130        $crate::defer!(f);
131    };
132}