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 result = $block;
79 result
81 }};
82}
83
84#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
85pub enum Visibility {
86 All,
87 None,
88 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}