1use syntax::ast::InspectMode;
4
5pub struct Phrase {
7 pub mode: InspectMode,
8 pub pattern: Vec<u8>,
9 pub by: Option<Vec<u8>>,
10 pub start: usize,
12 pub end: usize,
13}
14
15pub fn region(data: &[u8], before: Option<&[u8]>, after: Option<&[u8]>) -> (usize, usize) {
19 let find = |needle: &[u8]| (!needle.is_empty()).then(|| data.windows(needle.len()).position(|w| w == needle)).flatten();
20 let start = match after {
21 Some(a) => find(a).map_or(data.len(), |p| p + a.len()),
22 None => 0,
23 };
24 let end = match before {
25 Some(b) => find(b).unwrap_or(data.len()),
26 None => data.len(),
27 };
28 (start, end.max(start))
29}
30
31pub fn inspect(data: &mut [u8], phrases: &[Phrase]) -> Vec<i64> {
35 let mut counts = vec![0i64; phrases.len()];
36 let mut active = vec![true; phrases.len()];
37 let mut next_leading: Vec<usize> = phrases.iter().map(|p| p.start).collect();
38 let mut at = 0;
39 while at < data.len() {
40 let mut taken = 0;
41 for (k, phrase) in phrases.iter().enumerate() {
42 if !active[k] || at < phrase.start || at >= phrase.end {
43 continue;
44 }
45 if phrase.mode == InspectMode::Leading && at != next_leading[k] {
46 active[k] = false;
47 continue;
48 }
49 let len = if phrase.mode == InspectMode::Characters { 1 } else { phrase.pattern.len() };
50 let fits = len > 0 && at + len <= phrase.end;
51 let hit = fits && (phrase.mode == InspectMode::Characters || data[at..at + len] == phrase.pattern[..]);
52 if !hit {
53 if phrase.mode == InspectMode::Leading {
54 active[k] = false;
55 }
56 continue;
57 }
58 counts[k] += 1;
59 if let Some(by) = &phrase.by {
60 for (i, b) in by.iter().cycle().take(len).enumerate() {
61 data[at + i] = *b;
62 }
63 }
64 match phrase.mode {
65 InspectMode::First => active[k] = false,
66 InspectMode::Leading => next_leading[k] = at + len,
67 _ => {}
68 }
69 taken = len;
70 break;
71 }
72 at += taken.max(1);
73 }
74 counts
75}
76
77pub fn next_delimiter(source: &[u8], from: usize, delimiters: &[(bool, Vec<u8>)]) -> Option<(usize, usize)> {
80 (from..source.len()).find_map(|p| delimiters.iter().position(|(_, d)| !d.is_empty() && source[p..].starts_with(d)).map(|k| (p, k)))
81}
82
83pub fn past_delimiter(source: &[u8], at: usize, delimiter: &[u8], all: bool) -> usize {
85 let mut end = at + delimiter.len();
86 while all && !delimiter.is_empty() && source[end..].starts_with(delimiter) {
87 end += delimiter.len();
88 }
89 end
90}
91
92pub fn delimited(bytes: &[u8], delimiter: Option<&[u8]>) -> Vec<u8> {
94 match delimiter {
95 Some(d) if !d.is_empty() => {
96 let end = bytes.windows(d.len()).position(|w| w == d).unwrap_or(bytes.len());
97 bytes[..end].to_vec()
98 }
99 _ => bytes.to_vec(),
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 fn phrase(mode: InspectMode, pattern: &str, by: Option<&str>, region: (usize, usize)) -> Phrase {
108 Phrase { mode, pattern: pattern.as_bytes().to_vec(), by: by.map(|b| b.as_bytes().to_vec()), start: region.0, end: region.1 }
109 }
110
111 #[test]
112 fn tallying_all_leading_and_characters() {
113 let mut data = b" AABA ".to_vec();
114 let n = data.len();
115 let counts = inspect(&mut data, &[phrase(InspectMode::Leading, " ", None, (0, n)), phrase(InspectMode::All, "A", None, (0, n))]);
116 assert_eq!(counts, [2, 3]);
117 let counts = inspect(&mut data, &[phrase(InspectMode::Characters, "", None, (0, n))]);
118 assert_eq!(counts, [8]);
119 }
120
121 #[test]
122 fn replacing_first_all_and_within_bounds() {
123 let mut data = b"A,B,C.D,E".to_vec();
124 let (start, end) = region(&data, Some(b"."), None);
125 inspect(&mut data, &[phrase(InspectMode::All, ",", Some(";"), (start, end))]);
126 assert_eq!(data, b"A;B;C.D,E");
127 let mut data = b"XAXAX".to_vec();
128 inspect(&mut data, &[phrase(InspectMode::First, "X", Some("Y"), (0, 5))]);
129 assert_eq!(data, b"YAXAX");
130 let mut data = b"00012".to_vec();
131 inspect(&mut data, &[phrase(InspectMode::Leading, "0", Some(" "), (0, 5))]);
132 assert_eq!(data, b" 12");
133 }
134
135 #[test]
136 fn after_initial_with_no_occurrence_leaves_nothing() {
137 assert_eq!(region(b"ABC", None, Some(b"Z")), (3, 3));
138 assert_eq!(region(b"ABCD", Some(b"D"), Some(b"A")), (1, 3));
139 }
140
141 #[test]
142 fn a_leading_run_ends_at_the_first_other_character() {
143 let mut data = b"**A**".to_vec();
144 assert_eq!(inspect(&mut data, &[phrase(InspectMode::Leading, "*", None, (0, 5))]), [2]);
145 }
146
147 #[test]
148 fn delimiters() {
149 let d = vec![(true, b" ".to_vec()), (false, b",".to_vec())];
150 assert_eq!(next_delimiter(b"AB C,D", 0, &d), Some((2, 0)));
151 assert_eq!(past_delimiter(b"AB C,D", 2, b" ", true), 4);
152 assert_eq!(next_delimiter(b"AB C,D", 4, &d), Some((5, 1)));
153 assert_eq!(delimited(b"JOHN SMITH", Some(b" ")), b"JOHN");
154 }
155}