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