1use std::fmt;
2
3use crate::coding::{Decode, DecodeError, Encode};
4
5#[derive(Debug, Clone)]
6pub enum Filter {
7 Any,
9
10 Exact(String),
12
13 Prefix(String),
15
16 Suffix(String),
18
19 Wildcard { prefix: String, suffix: String },
22}
23
24impl Filter {
25 pub fn new(pattern: &str) -> Self {
26 if pattern.is_empty() {
27 return Self::Any;
28 }
29
30 if let Some((prefix, suffix)) = pattern.split_once("*") {
31 return match (prefix.is_empty(), suffix.is_empty()) {
32 (true, true) => Self::Any,
33 (true, false) => Self::Suffix(suffix.to_string()),
34 (false, true) => Self::Prefix(prefix.to_string()),
35 (false, false) => Self::Wildcard {
36 prefix: prefix.to_string(),
37 suffix: suffix.to_string(),
38 },
39 };
40 }
41
42 Self::Exact(pattern.to_string())
43 }
44
45 pub fn matches<'a>(&self, input: &'a str) -> Option<FilterMatch<'a>> {
49 match self {
50 Self::Any => Some(FilterMatch {
51 full: input,
52 capture: (0, input.len()),
53 }),
54 Self::Exact(pattern) if input == pattern => Some(FilterMatch {
55 full: input,
56 capture: (0, 0),
57 }),
58 Self::Prefix(prefix) if input.starts_with(prefix) => Some(FilterMatch {
59 full: input,
60 capture: (prefix.len(), input.len()),
61 }),
62 Self::Suffix(suffix) if input.ends_with(suffix) => Some(FilterMatch {
63 full: input,
64 capture: (0, input.len() - suffix.len()),
65 }),
66 Self::Wildcard { prefix, suffix }
67 if input.len() >= prefix.len() + suffix.len()
68 && input.starts_with(prefix)
69 && input.ends_with(suffix) =>
70 {
71 Some(FilterMatch {
72 full: input,
73 capture: (prefix.len(), input.len() - suffix.len()),
74 })
75 }
76 _ => None,
77 }
78 }
79
80 pub fn reconstruct(&self, capture: &str) -> String {
82 match self {
83 Self::Any => capture.to_string(),
84 Self::Exact(pattern) => pattern.to_string(),
85 Self::Prefix(prefix) => format!("{}{}", prefix, capture),
86 Self::Suffix(suffix) => format!("{}{}", capture, suffix),
87 Self::Wildcard { prefix, suffix } => format!("{}{}{}", prefix, capture, suffix),
88 }
89 }
90}
91
92impl<T: AsRef<str>> From<T> for Filter {
93 fn from(pattern: T) -> Self {
94 Self::new(pattern.as_ref())
95 }
96}
97
98impl Encode for Filter {
99 fn encode<W: bytes::BufMut>(&self, w: &mut W) {
100 match self {
101 Self::Any => {
102 0u64.encode(w);
103 }
104 Self::Exact(pattern) => {
105 pattern.encode(w);
106 }
107 Self::Prefix(prefix) => {
108 (prefix.len() + 1).encode(w);
109 w.put(prefix.as_bytes());
110 w.put(&b"*"[..]);
111 }
112 Self::Suffix(suffix) => {
113 (suffix.len() + 1).encode(w);
114 w.put(&b"*"[..]);
115 w.put(suffix.as_bytes());
116 }
117 Self::Wildcard { prefix, suffix } => {
118 (prefix.len() + suffix.len() + 1).encode(w);
119 w.put(prefix.as_bytes());
120 w.put(&b"*"[..]);
121 w.put(suffix.as_bytes());
122 }
123 }
124 }
125}
126
127impl Decode for Filter {
128 fn decode<R: bytes::Buf>(r: &mut R) -> Result<Self, DecodeError> {
129 let pattern = String::decode(r)?;
130 Ok(Self::new(&pattern))
131 }
132}
133
134#[cfg(test)]
135impl Filter {
136 fn assert(&self, input: &str, expected: Option<&str>) {
137 let fm = self.matches(input).map(|r| r.capture());
138 assert_eq!(fm, expected);
139 }
140}
141
142#[derive(PartialEq, Eq)]
143pub struct FilterMatch<'a> {
144 full: &'a str,
145 capture: (usize, usize),
147}
148
149impl<'a> FilterMatch<'a> {
150 pub fn full(&self) -> &'a str {
151 self.full
152 }
153
154 pub fn capture(&self) -> &'a str {
155 &self.full[self.capture.0..self.capture.1]
156 }
157
158 pub fn capture_index(&self) -> (usize, usize) {
160 self.capture
161 }
162}
163
164impl fmt::Debug for FilterMatch<'_> {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.debug_struct("FilterMatch")
167 .field("full", &self.full())
168 .field("capture", &self.capture())
169 .finish()
170 }
171}
172
173#[cfg(test)]
174mod test {
175 use super::*;
176
177 #[test]
178 fn prefix() {
179 let filter = Filter::new("*/bar/baz");
180 filter.assert("foo/bar/baz", Some("foo"));
181 filter.assert("foo/bar/", None);
182 filter.assert("foo/bar/baz/qux", None);
183 filter.assert("zoo/bar/baz", Some("zoo"));
184 }
185
186 #[test]
187 fn middle() {
188 let filter = Filter::new("foo/*/baz");
189 filter.assert("foo/bar/baz", Some("bar"));
190 filter.assert("foo/bar/", None);
191 filter.assert("foo/bar/baz/qux", None);
192 filter.assert("zoo/bar/baz", None);
193 }
194
195 #[test]
196 fn suffix() {
197 let filter = Filter::new("foo/bar/*");
198 filter.assert("foo/bar/baz", Some("baz"));
199 filter.assert("foo/bar/", Some(""));
200 filter.assert("foo/bar/baz/qux", Some("baz/qux"));
201 filter.assert("zoo/bar/baz", None);
202 }
203
204 #[test]
205 fn literal() {
206 let filter = Filter::new("foo/bar/baz");
207 filter.assert("foo/bar/baz", Some(""));
208 filter.assert("foo/bar/", None);
209 filter.assert("foo/bar/baz/qux", None);
210 filter.assert("zoo/bar/baz", None);
211 }
212}