1use crate::error::*;
2
3#[derive(Debug, Clone)]
4pub struct LineRange {
5 lower: usize,
6 upper: usize,
7}
8
9impl Default for LineRange {
10 fn default() -> LineRange {
11 LineRange {
12 lower: usize::min_value(),
13 upper: usize::max_value(),
14 }
15 }
16}
17
18impl LineRange {
19 pub fn new(from: usize, to: usize) -> Self {
20 LineRange {
21 lower: from,
22 upper: to,
23 }
24 }
25
26 pub fn from(range_raw: &str) -> Result<LineRange> {
27 LineRange::parse_range(range_raw)
28 }
29
30 fn parse_range(range_raw: &str) -> Result<LineRange> {
31 let mut new_range = LineRange::default();
32
33 if range_raw.bytes().next().ok_or("Empty line range")? == b':' {
34 new_range.upper = range_raw[1..].parse()?;
35 return Ok(new_range);
36 } else if range_raw.bytes().last().ok_or("Empty line range")? == b':' {
37 new_range.lower = range_raw[..range_raw.len() - 1].parse()?;
38 return Ok(new_range);
39 }
40
41 let line_numbers: Vec<&str> = range_raw.split(':').collect();
42 match line_numbers.len() {
43 1 => {
44 new_range.lower = line_numbers[0].parse()?;
45 new_range.upper = new_range.lower;
46 Ok(new_range)
47 }
48 2 => {
49 new_range.lower = line_numbers[0].parse()?;
50 let first_byte = line_numbers[1].bytes().next();
51
52 new_range.upper = if first_byte == Some(b'+') {
53 let more_lines = &line_numbers[1][1..]
54 .parse()
55 .map_err(|_| "Invalid character after +")?;
56 new_range.lower + more_lines
57 } else if first_byte == Some(b'-') {
58 if line_numbers[1][1..].bytes().next() == Some(b'+') {
60 return Err("Invalid character after -".into());
61 }
62 let prior_lines = &line_numbers[1][1..]
63 .parse()
64 .map_err(|_| "Invalid character after -")?;
65 let prev_lower = new_range.lower;
66 new_range.lower = new_range.lower.saturating_sub(*prior_lines);
67 prev_lower
68 } else {
69 line_numbers[1].parse()?
70 };
71
72 Ok(new_range)
73 }
74 _ => Err(
75 "Line range contained more than one ':' character. Expected format: 'N' or 'N:M'"
76 .into(),
77 ),
78 }
79 }
80
81 pub(crate) fn is_inside(&self, line: usize) -> bool {
82 line >= self.lower && line <= self.upper
83 }
84}
85
86#[test]
87fn test_parse_full() {
88 let range = LineRange::from("40:50").expect("Shouldn't fail on test!");
89 assert_eq!(40, range.lower);
90 assert_eq!(50, range.upper);
91}
92
93#[test]
94fn test_parse_partial_min() {
95 let range = LineRange::from(":50").expect("Shouldn't fail on test!");
96 assert_eq!(usize::min_value(), range.lower);
97 assert_eq!(50, range.upper);
98}
99
100#[test]
101fn test_parse_partial_max() {
102 let range = LineRange::from("40:").expect("Shouldn't fail on test!");
103 assert_eq!(40, range.lower);
104 assert_eq!(usize::max_value(), range.upper);
105}
106
107#[test]
108fn test_parse_single() {
109 let range = LineRange::from("40").expect("Shouldn't fail on test!");
110 assert_eq!(40, range.lower);
111 assert_eq!(40, range.upper);
112}
113
114#[test]
115fn test_parse_fail() {
116 let range = LineRange::from("40:50:80");
117 assert!(range.is_err());
118 let range = LineRange::from("40::80");
119 assert!(range.is_err());
120 let range = LineRange::from(":40:");
121 assert!(range.is_err());
122}
123
124#[test]
125fn test_parse_plus() {
126 let range = LineRange::from("40:+10").expect("Shouldn't fail on test!");
127 assert_eq!(40, range.lower);
128 assert_eq!(50, range.upper);
129}
130
131#[test]
132fn test_parse_plus_fail() {
133 let range = LineRange::from("40:+z");
134 assert!(range.is_err());
135 let range = LineRange::from("40:+-10");
136 assert!(range.is_err());
137 let range = LineRange::from("40:+");
138 assert!(range.is_err());
139}
140
141#[test]
142fn test_parse_minus_success() {
143 let range = LineRange::from("40:-10").expect("Shouldn't fail on test!");
144 assert_eq!(30, range.lower);
145 assert_eq!(40, range.upper);
146}
147
148#[test]
149fn test_parse_minus_edge_cases_success() {
150 let range = LineRange::from("5:-4").expect("Shouldn't fail on test!");
151 assert_eq!(1, range.lower);
152 assert_eq!(5, range.upper);
153 let range = LineRange::from("5:-5").expect("Shouldn't fail on test!");
154 assert_eq!(0, range.lower);
155 assert_eq!(5, range.upper);
156 let range = LineRange::from("5:-100").expect("Shouldn't fail on test!");
157 assert_eq!(0, range.lower);
158 assert_eq!(5, range.upper);
159}
160
161#[test]
162fn test_parse_minus_fail() {
163 let range = LineRange::from("40:-z");
164 assert!(range.is_err());
165 let range = LineRange::from("40:-+10");
166 assert!(range.is_err());
167 let range = LineRange::from("40:-");
168 assert!(range.is_err());
169}
170
171#[derive(Copy, Clone, Debug, PartialEq)]
172pub enum RangeCheckResult {
173 InRange,
175
176 BeforeOrBetweenRanges,
178
179 AfterLastRange,
181}
182
183#[derive(Debug, Clone)]
184pub struct LineRanges {
185 ranges: Vec<LineRange>,
186 largest_upper_bound: usize,
187}
188
189impl LineRanges {
190 pub fn none() -> LineRanges {
191 LineRanges::from(vec![])
192 }
193
194 pub fn all() -> LineRanges {
195 LineRanges::from(vec![LineRange::default()])
196 }
197
198 pub fn from(ranges: Vec<LineRange>) -> LineRanges {
199 let largest_upper_bound = ranges
200 .iter()
201 .map(|r| r.upper)
202 .max()
203 .unwrap_or(usize::max_value());
204 LineRanges {
205 ranges,
206 largest_upper_bound,
207 }
208 }
209
210 pub(crate) fn check(&self, line: usize) -> RangeCheckResult {
211 if self.ranges.iter().any(|r| r.is_inside(line)) {
212 RangeCheckResult::InRange
213 } else if line < self.largest_upper_bound {
214 RangeCheckResult::BeforeOrBetweenRanges
215 } else {
216 RangeCheckResult::AfterLastRange
217 }
218 }
219}
220
221impl Default for LineRanges {
222 fn default() -> Self {
223 Self::all()
224 }
225}
226
227#[derive(Debug, Clone)]
228pub struct HighlightedLineRanges(pub LineRanges);
229
230impl Default for HighlightedLineRanges {
231 fn default() -> Self {
232 HighlightedLineRanges(LineRanges::none())
233 }
234}
235
236#[cfg(test)]
237fn ranges(rs: &[&str]) -> LineRanges {
238 LineRanges::from(rs.iter().map(|r| LineRange::from(r).unwrap()).collect())
239}
240
241#[test]
242fn test_ranges_simple() {
243 let ranges = ranges(&["3:8"]);
244
245 assert_eq!(RangeCheckResult::BeforeOrBetweenRanges, ranges.check(2));
246 assert_eq!(RangeCheckResult::InRange, ranges.check(5));
247 assert_eq!(RangeCheckResult::AfterLastRange, ranges.check(9));
248}
249
250#[test]
251fn test_ranges_advanced() {
252 let ranges = ranges(&["3:8", "11:20", "25:30"]);
253
254 assert_eq!(RangeCheckResult::BeforeOrBetweenRanges, ranges.check(2));
255 assert_eq!(RangeCheckResult::InRange, ranges.check(5));
256 assert_eq!(RangeCheckResult::BeforeOrBetweenRanges, ranges.check(9));
257 assert_eq!(RangeCheckResult::InRange, ranges.check(11));
258 assert_eq!(RangeCheckResult::BeforeOrBetweenRanges, ranges.check(22));
259 assert_eq!(RangeCheckResult::InRange, ranges.check(28));
260 assert_eq!(RangeCheckResult::AfterLastRange, ranges.check(31));
261}
262
263#[test]
264fn test_ranges_open_low() {
265 let ranges = ranges(&["3:8", ":5"]);
266
267 assert_eq!(RangeCheckResult::InRange, ranges.check(1));
268 assert_eq!(RangeCheckResult::InRange, ranges.check(3));
269 assert_eq!(RangeCheckResult::InRange, ranges.check(7));
270 assert_eq!(RangeCheckResult::AfterLastRange, ranges.check(9));
271}
272
273#[test]
274fn test_ranges_open_high() {
275 let ranges = ranges(&["3:", "2:5"]);
276
277 assert_eq!(RangeCheckResult::BeforeOrBetweenRanges, ranges.check(1));
278 assert_eq!(RangeCheckResult::InRange, ranges.check(3));
279 assert_eq!(RangeCheckResult::InRange, ranges.check(5));
280 assert_eq!(RangeCheckResult::InRange, ranges.check(9));
281}
282
283#[test]
284fn test_ranges_all() {
285 let ranges = LineRanges::all();
286
287 assert_eq!(RangeCheckResult::InRange, ranges.check(1));
288}
289
290#[test]
291fn test_ranges_none() {
292 let ranges = LineRanges::none();
293
294 assert_ne!(RangeCheckResult::InRange, ranges.check(1));
295}