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
110
use {
smallvec::*,
std::{
num::ParseIntError,
str::FromStr,
},
thiserror::Error,
};
#[derive(Debug, Error)]
pub enum StatusFilterParseError {
#[error("invalid int")]
ParseInt(#[from] ParseIntError),
}
#[derive(Debug, Clone)]
pub struct StatusFilter {
include: SmallVec<[(u16, u16); 4]>,
exclude: SmallVec<[(u16, u16); 4]>,
}
fn ranges_contains(ranges: &[(u16, u16)], status: u16) -> bool {
for range in ranges {
if range.0 <= status && status <= range.1 {
return true;
}
}
false
}
fn parse_range(s: &str) -> Result<(u16, u16), StatusFilterParseError> {
Ok(match s {
"2xx" => (200, 299),
"3xx" => (300, 399),
"4xx" => (400, 499),
"5xx" => (500, 599),
s if s.contains('-') => {
let mut tokens = s.split('-');
(
tokens.next().unwrap().parse()?,
tokens.next().unwrap().parse()?,
)
}
s => {
let v = s.parse()?;
(v, v)
}
})
}
impl StatusFilter {
pub fn accepts(&self, status: u16) -> bool {
if ranges_contains(&self.exclude, status) {
false
} else if self.include.is_empty() {
true
} else {
ranges_contains(&self.include, status)
}
}
}
impl FromStr for StatusFilter {
type Err= StatusFilterParseError;
fn from_str(value: &str) -> Result<Self, StatusFilterParseError> {
let mut include = SmallVec::new();
let mut exclude = SmallVec::new();
for s in value.split(',') {
if let Some(s) = s.strip_prefix('!') {
exclude.push(parse_range(s)?);
} else {
include.push(parse_range(s)?);
}
}
Ok(Self { include, exclude })
}
}
#[cfg(test)]
mod status_filter_tests {
use super::*;
#[test]
fn test_status_filter() {
let sf = StatusFilter::from_str("400").unwrap();
assert_eq!(sf.accepts(400), true);
assert_eq!(sf.accepts(401), false);
let sf = StatusFilter::from_str("2xx,405-512").unwrap();
assert_eq!(sf.accepts(200), true);
assert_eq!(sf.accepts(299), true);
assert_eq!(sf.accepts(300), false);
assert_eq!(sf.accepts(400), false);
assert_eq!(sf.accepts(405), true);
assert_eq!(sf.accepts(512), true);
assert_eq!(sf.accepts(513), false);
let sf = StatusFilter::from_str("4xx,!404").unwrap();
assert_eq!(sf.accepts(200), false);
assert_eq!(sf.accepts(400), true);
assert_eq!(sf.accepts(404), false);
assert_eq!(sf.accepts(421), true);
}
}