1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use crate::agnostic::Clump;
use itertools::Itertools;
#[cfg(feature = "regex")]
use regex::Regex;
/// A specialised version of the [`Clod`](crate::agnostic::Clod) trait, for formats where every
/// entry is guaranteed to be a [`Clump`] of data, and subdirectories are not possible.
///
/// The Doom WAD format is one of these.
pub trait FlatClod<Q> where Q: Clump {
/// Get the amount of clumps.
fn clump_amt(&self) -> usize;
/// Borrows a single clump by number.
fn nth_clump(&self, n: usize) -> &Q;
/// Borrows many clumps by numbers. The range may be non-contiguous. The returned clumps are always
/// in numerically increasing order, and are not duplicated, regardless of the ordering or
/// overlapping of the provided range(s).
fn a_sub_nth_clumps(&self, a: impl Iterator<Item = usize>) -> Vec<&Q>;
// -------------------------------------------------------------------------------------------------------
// PROVIDED:
/// Borrows every clump.
fn all_clumps(&self) -> Vec<&Q> {
self.a_sub_nth_clumps(0..self.clump_amt())
}
/// Indices of clumps with the specified name.
fn which_clumps(&self, s: impl AsRef<str>) -> Vec<usize> {
self.all_clumps().into_iter()
.enumerate()
.filter(|(_k, l)| l.name() == s.as_ref())
.map(|(k, _l)| k)
.collect()
}
/// Get every clump with the specified name. The clumps will be in numerically increasing order
/// by their index in the wad.
fn clumps_with_name(&self, s: impl AsRef<str>) -> Vec<&Q> {
self.a_sub_nth_clumps(self.which_clumps(s).into_iter())
}
/// Get a clump by it's specific name. If multiple clumps with the name exist, returns the first one.
/// If no clumps with the name exist, returns [`None`].
fn clump_by_name(&self, s: impl AsRef<str>) -> Option<&Q> {
// todo: can it be done with a one-liner? (i.e. no `let`?)
let v = self.clumps_with_name(s);
if v.len() == 0 { None } else { Some(v[0]) }
}
/// Indices of clumps whose names match the given regex.
#[cfg(feature = "regex")]
fn which_clumps_re(&self, r: Regex) -> Vec<usize> {
self.all_clumps().into_iter()
.enumerate()
.filter(|(_k, l)| r.is_match(l.name()))
.map(|(k, _l)| k)
.collect()
}
/// Get every clump with a name matching the regex. The clumps will be in numerically increasing order
/// by their index in the wad.
#[cfg(feature = "regex")]
fn clumps_with_name_re(&self, r: Regex) -> Vec<&Q> {
self.a_sub_nth_clumps(self.which_clumps_re(r).into_iter())
}
/// Get a clump, matching with a specific regex. If multiple matching clumps exist, returns the first one.
/// If no clumps matching the pattern exist, returns [`None`].
#[cfg(feature = "regex")]
fn clump_by_name_re(&self, r: Regex) -> Option<&Q> {
// todo: can it be done with a one-liner? (i.e. no `let`?)
let v = self.clumps_with_name_re(r);
if v.len() == 0 { None } else { Some(v[0]) }
}
/// Returns the indices of clumps between the given markers, sorted numerically.
fn indices_between_markers(&self, start: impl AsRef<str>, end: impl AsRef<str>) -> Vec<usize> {
let start_marks = self.which_clumps(start);
let end_marks = self.which_clumps(end);
#[derive(Clone, Copy, PartialEq, Eq)]
enum MarkType {
Start, End
}
let mut u = vec!();
for (a, b) in start_marks.into_iter().map(|n| (n, MarkType::Start))
.chain(end_marks.into_iter().map(|n| (n, MarkType::End)))
.sorted_by(|(n, _), (k, _)| n.cmp(&k))
.tuple_windows() {
if b.1 == MarkType::End && a.1 == MarkType::Start {
u.append(&mut ((a.0 + 1)..b.0).collect());
}
}
u
}
/// Borrows all clumps between the given markers, sorted numerically by index.
fn clumps_between_markers(&self, start: impl AsRef<str>, end: impl AsRef<str>) -> Vec<&Q> {
self.a_sub_nth_clumps(self.indices_between_markers(start, end).iter().map(|n| *n))
}
}