Skip to main content

graphitepdf_utils/
array.rs

1#[derive(Clone, Debug, PartialEq, Eq)]
2pub enum OneOrMany<T> {
3    One(T),
4    Many(Vec<T>),
5}
6
7impl<T> From<T> for OneOrMany<T> {
8    fn from(value: T) -> Self {
9        Self::One(value)
10    }
11}
12
13impl<T> From<Vec<T>> for OneOrMany<T> {
14    fn from(value: Vec<T>) -> Self {
15        Self::Many(value)
16    }
17}
18
19pub fn cast_array<T>(input: impl Into<OneOrMany<T>>) -> Vec<T> {
20    match input.into() {
21        OneOrMany::One(value) => vec![value],
22        OneOrMany::Many(values) => values,
23    }
24}
25
26pub fn adjust<T, F>(index: isize, mut adjuster: F, values: &[T]) -> Vec<T>
27where
28    T: Clone,
29    F: FnMut(T) -> T,
30{
31    let mut result = values.to_vec();
32    let len = result.len();
33
34    if let Some(index) = normalize_index(index, len) {
35        result[index] = adjuster(result[index].clone());
36    }
37
38    result
39}
40
41pub trait DropLast {
42    type Output;
43
44    fn drop_last(self) -> Self::Output;
45}
46
47pub fn drop_last<T>(value: T) -> T::Output
48where
49    T: DropLast,
50{
51    value.drop_last()
52}
53
54impl<T: Clone> DropLast for &[T] {
55    type Output = Vec<T>;
56
57    fn drop_last(self) -> Self::Output {
58        self[..self.len().saturating_sub(1)].to_vec()
59    }
60}
61
62impl<T> DropLast for Vec<T> {
63    type Output = Vec<T>;
64
65    fn drop_last(mut self) -> Self::Output {
66        let _ = self.pop();
67        self
68    }
69}
70
71impl DropLast for &str {
72    type Output = String;
73
74    fn drop_last(self) -> Self::Output {
75        let mut result = self.to_string();
76        let _ = result.pop();
77        result
78    }
79}
80
81impl DropLast for String {
82    type Output = String;
83
84    fn drop_last(mut self) -> Self::Output {
85        let _ = self.pop();
86        self
87    }
88}
89
90pub trait Last {
91    type Output;
92
93    fn last_value(self) -> Option<Self::Output>;
94}
95
96pub fn last<T>(value: T) -> Option<T::Output>
97where
98    T: Last,
99{
100    value.last_value()
101}
102
103impl<T: Clone> Last for &[T] {
104    type Output = T;
105
106    fn last_value(self) -> Option<Self::Output> {
107        self.last().cloned()
108    }
109}
110
111impl<T> Last for Vec<T> {
112    type Output = T;
113
114    fn last_value(self) -> Option<Self::Output> {
115        self.into_iter().last()
116    }
117}
118
119impl Last for &str {
120    type Output = char;
121
122    fn last_value(self) -> Option<Self::Output> {
123        self.chars().next_back()
124    }
125}
126
127impl Last for String {
128    type Output = char;
129
130    fn last_value(self) -> Option<Self::Output> {
131        self.chars().next_back()
132    }
133}
134
135pub fn repeat<T>(value: T, count: usize) -> Vec<T>
136where
137    T: Clone,
138{
139    vec![value; count]
140}
141
142pub trait Reverse {
143    type Output;
144
145    fn reverse_value(self) -> Self::Output;
146}
147
148pub fn reverse<T>(value: T) -> T::Output
149where
150    T: Reverse,
151{
152    value.reverse_value()
153}
154
155impl<T: Clone> Reverse for &[T] {
156    type Output = Vec<T>;
157
158    fn reverse_value(self) -> Self::Output {
159        self.iter().cloned().rev().collect()
160    }
161}
162
163impl<T> Reverse for Vec<T> {
164    type Output = Vec<T>;
165
166    fn reverse_value(mut self) -> Self::Output {
167        self.reverse();
168        self
169    }
170}
171
172pub fn without<T>(excluded: &[T], values: &[T]) -> Vec<T>
173where
174    T: Clone + PartialEq,
175{
176    values
177        .iter()
178        .filter(|value| !excluded.iter().any(|excluded| excluded == *value))
179        .cloned()
180        .collect()
181}
182
183fn normalize_index(index: isize, len: usize) -> Option<usize> {
184    if len == 0 {
185        return None;
186    }
187
188    if index >= 0 {
189        let index = usize::try_from(index).ok()?;
190        return (index < len).then_some(index);
191    }
192
193    let len = isize::try_from(len).ok()?;
194    let adjusted = len.checked_add(index)?;
195
196    if adjusted < 0 {
197        None
198    } else {
199        usize::try_from(adjusted).ok()
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    #[test]
208    fn casts_single_values_and_vectors() {
209        assert_eq!(cast_array("foo"), vec!["foo"]);
210        assert_eq!(cast_array::<&str>(vec!["foo"]), vec!["foo"]);
211    }
212
213    #[test]
214    fn adjusts_values_using_positive_and_negative_indexes() {
215        assert_eq!(adjust(1, |value| value * 2, &[1, 2, 3]), vec![1, 4, 3]);
216        assert_eq!(adjust(-1, |value| value + 10, &[1, 2, 3]), vec![1, 2, 13]);
217    }
218
219    #[test]
220    fn drops_last_for_slices_and_strings() {
221        assert_eq!(drop_last(&[1, 2, 3][..]), vec![1, 2]);
222        assert_eq!(drop_last("hello"), "hell");
223    }
224
225    #[test]
226    fn gets_last_for_slices_and_strings() {
227        assert_eq!(last(&[1, 2, 3][..]), Some(3));
228        assert_eq!(last("abc"), Some('c'));
229        assert_eq!(last(&[] as &[i32]), None);
230    }
231
232    #[test]
233    fn repeats_reverses_and_filters_values() {
234        assert_eq!(repeat("a", 3), vec!["a", "a", "a"]);
235        assert_eq!(reverse(&[1, 2, 3][..]), vec![3, 2, 1]);
236        assert_eq!(without(&[2, 4], &[1, 2, 3, 4, 5]), vec![1, 3, 5]);
237    }
238}