leftwm_core/utils/
helpers.rs1use std::cmp::Ordering;
3
4pub fn intersect<T>(v1: &[T], v2: &[T]) -> bool
5where
6 T: PartialEq,
7{
8 for a in v1 {
9 for b in v2 {
10 if a == b {
11 return true;
12 }
13 }
14 }
15 false
16}
17
18pub fn vec_extract<T, F>(list: &mut Vec<T>, test: F) -> Vec<T>
19where
20 F: Fn(&T) -> bool,
21 T: Clone,
22{
23 let len = list.len();
24 let mut removed = vec![];
25 let mut del = 0;
26 {
27 let v = &mut **list;
28
29 for i in 0..len {
30 if test(&v[i]) {
31 removed.push(v[i].clone());
32 del += 1;
33 } else if del > 0 {
34 v.swap(i - del, i);
35 }
36 }
37 }
38 list.truncate(len - del);
39 removed
40}
41
42pub fn cycle_vec<T>(list: &mut Vec<T>, shift: i32) -> Option<()>
43where
44 T: Clone,
45{
46 let v = &mut **list;
47 let change = shift.unsigned_abs() as usize;
48 if v.len() < change {
49 return None;
50 }
51 match shift.cmp(&0) {
52 Ordering::Less => v.rotate_left(change),
53 Ordering::Greater => v.rotate_right(change),
54 Ordering::Equal => {}
55 }
56 Some(())
57}
58
59pub fn reorder_vec<T, F>(list: &mut Vec<T>, test: F, shift: i32) -> Option<()>
61where
62 F: Fn(&T) -> bool,
63 T: Clone,
64{
65 let len = list.len() as i32;
66 if len < 2 {
67 return None;
68 }
69 let index = list.iter().position(test)?;
70 let item = list.get(index)?.clone();
71
72 let mut new_index = index as i32 + shift;
73 list.remove(index);
74 let v = &mut **list;
75
76 if new_index < 0 {
77 new_index += len;
78 v.rotate_right(1);
79 } else if new_index >= len {
80 new_index -= len;
81 v.rotate_left(1);
82 }
83 list.insert(new_index as usize, item);
84 Some(())
85}
86
87pub fn relative_find<T, F>(
105 list: &[T],
106 reference_finder: F,
107 shift: i32,
108 should_loop: bool,
109) -> Option<&T>
110where
111 F: Fn(&T) -> bool,
112{
113 let len = list.len() as i32;
114 let reference_index = list.iter().position(reference_finder)?;
115 let loops = if shift.is_negative() {
116 shift.unsigned_abs() as usize > reference_index
118 } else {
119 shift as usize > len as usize - (reference_index + 1)
121 };
122
123 let relative_index = if loops && !should_loop {
124 None
125 } else {
126 let shift = shift % len;
127 let shifted_index = reference_index as i32 + shift;
128 let max_index = len - 1;
129 if shifted_index < 0 {
130 Some((len + shifted_index) as usize)
131 } else if shifted_index > max_index {
132 Some((shifted_index - len) as usize)
133 } else {
134 Some(shifted_index as usize)
135 }
136 }?;
137
138 list.get(relative_index)
139}
140
141#[cfg(test)]
142pub(crate) mod test {
143 use crate::utils::helpers::relative_find;
144
145 pub async fn temp_path() -> std::io::Result<std::path::PathBuf> {
146 tokio::task::spawn_blocking(|| tempfile::Builder::new().tempfile_in("../target"))
147 .await
148 .expect("Blocking task joined")?
149 .into_temp_path()
150 .keep()
151 .map_err(Into::into)
152 }
153
154 #[test]
155 fn relative_find_should_work_both_ways() {
156 let list = vec!["hello", "world", "foo", "bar"];
157 let result = relative_find(&list, |&e| e == "hello", 2, false);
158 assert_eq!(result, Some(&"foo"));
159 let result = relative_find(&list, |&e| e == "bar", -2, false);
160 assert_eq!(result, Some(&"world"));
161 }
162
163 #[test]
164 fn relative_find_with_inexistent_reference_must_return_none() {
165 let list = vec!["hello", "world", "foo", "bar"];
166 let result = relative_find(&list, |&e| e == "inexistent", 2, false);
167 assert_eq!(result, None);
168 }
169
170 #[test]
171 fn relative_find_should_be_able_to_loop() {
172 let list = vec!["hello", "world", "foo", "bar"];
173 let result = relative_find(&list, |&e| e == "hello", 4, true);
174 assert_eq!(result, Some(&"hello"));
175 let result = relative_find(&list, |&e| e == "hello", 9, true);
176 assert_eq!(result, Some(&"world"));
177 let result = relative_find(&list, |&e| e == "hello", -9, true);
178 assert_eq!(result, Some(&"bar"));
179 }
180
181 #[test]
182 fn relative_find_loop_can_be_disabled() {
183 let list = vec!["hello", "world", "foo", "bar"];
184 let result = relative_find(&list, |&e| e == "hello", 9, false);
185 assert_eq!(result, None);
186 }
187}