@@ -38,6 +38,10 @@ impl LineRange {
3838
3939 Err("expected single ':' character".into())
4040 }
41+
42+ pub fn is_inside(&self, line: usize) -> bool {
43+ line >= self.lower && line <= self.upper
44+ }
4145}
4246
4347#[test]
@@ -72,3 +76,105 @@ fn test_parse_fail() {
7276 let range = LineRange::from("40");
7377 assert!(range.is_err());
7478}
79+
80+#[derive(Copy, Clone, Debug, PartialEq)]
81+pub enum RangeCheckResult {
82+ // Within one of the given ranges
83+ InRange,
84+
85+ // Before the first range or within two ranges
86+ OutsideRange,
87+
88+ // Line number is outside of all ranges and larger than the last range.
89+ AfterLastRange,
90+}
91+
92+#[derive(Clone)]
93+pub struct LineRanges {
94+ ranges: Vec<LineRange>,
95+ largest_upper_bound: usize,
96+}
97+
98+impl LineRanges {
99+ pub fn from(ranges: Vec<LineRange>) -> LineRanges {
100+ let largest_upper_bound = ranges
101+ .iter()
102+ .map(|r| r.upper)
103+ .max()
104+ .unwrap_or(usize::max_value());
105+ LineRanges {
106+ ranges,
107+ largest_upper_bound,
108+ }
109+ }
110+
111+ pub fn check(&self, line: usize) -> RangeCheckResult {
112+ if self.ranges.is_empty() {
113+ RangeCheckResult::InRange
114+ } else {
115+ if self.ranges.iter().any(|r| r.is_inside(line)) {
116+ RangeCheckResult::InRange
117+ } else {
118+ if line < self.largest_upper_bound {
119+ RangeCheckResult::OutsideRange
120+ } else {
121+ RangeCheckResult::AfterLastRange
122+ }
123+ }
124+ }
125+ }
126+}
127+
128+#[cfg(test)]
129+fn ranges(rs: &[&str]) -> LineRanges {
130+ LineRanges::from(rs.iter().map(|r| LineRange::from(r).unwrap()).collect())
131+}
132+
133+#[test]
134+fn test_ranges_simple() {
135+ let ranges = ranges(&["3:8"]);
136+
137+ assert_eq!(RangeCheckResult::OutsideRange, ranges.check(2));
138+ assert_eq!(RangeCheckResult::InRange, ranges.check(5));
139+ assert_eq!(RangeCheckResult::AfterLastRange, ranges.check(9));
140+}
141+
142+#[test]
143+fn test_ranges_advanced() {
144+ let ranges = ranges(&["3:8", "11:20", "25:30"]);
145+
146+ assert_eq!(RangeCheckResult::OutsideRange, ranges.check(2));
147+ assert_eq!(RangeCheckResult::InRange, ranges.check(5));
148+ assert_eq!(RangeCheckResult::OutsideRange, ranges.check(9));
149+ assert_eq!(RangeCheckResult::InRange, ranges.check(11));
150+ assert_eq!(RangeCheckResult::OutsideRange, ranges.check(22));
151+ assert_eq!(RangeCheckResult::InRange, ranges.check(28));
152+ assert_eq!(RangeCheckResult::AfterLastRange, ranges.check(31));
153+}
154+
155+#[test]
156+fn test_ranges_open_low() {
157+ let ranges = ranges(&["3:8", ":5"]);
158+
159+ assert_eq!(RangeCheckResult::InRange, ranges.check(1));
160+ assert_eq!(RangeCheckResult::InRange, ranges.check(3));
161+ assert_eq!(RangeCheckResult::InRange, ranges.check(7));
162+ assert_eq!(RangeCheckResult::AfterLastRange, ranges.check(9));
163+}
164+
165+#[test]
166+fn test_ranges_open_high() {
167+ let ranges = ranges(&["3:", "2:5"]);
168+
169+ assert_eq!(RangeCheckResult::OutsideRange, ranges.check(1));
170+ assert_eq!(RangeCheckResult::InRange, ranges.check(3));
171+ assert_eq!(RangeCheckResult::InRange, ranges.check(5));
172+ assert_eq!(RangeCheckResult::InRange, ranges.check(9));
173+}
174+
175+#[test]
176+fn test_ranges_empty() {
177+ let ranges = ranges(&[]);
178+
179+ assert_eq!(RangeCheckResult::InRange, ranges.check(1));
180+}