1use std::iter;
4
5#[derive(Debug, Default)]
7pub(crate) struct PathMap<T> {
8 pub(crate) matchers: Vec<(String, T)>,
11}
12
13impl<T> PathMap<T> {
14 pub(crate) fn insert(&mut self, matcher: String, value: T) {
16 self.matchers.push((matcher, value));
17 }
18
19 pub(crate) fn get(&self, fq_path: &str) -> Iter<'_, T> {
21 Iter::new(self, fq_path.to_string())
22 }
23
24 pub(crate) fn get_field(&self, fq_path: &str, field: &str) -> Iter<'_, T> {
26 Iter::new(self, format!("{fq_path}.{field}"))
27 }
28
29 #[allow(unused)]
32 pub(crate) fn get_first<'a>(&'a self, fq_path: &'_ str) -> Option<&'a T> {
33 self.find_best_matching(fq_path)
34 }
35
36 pub(crate) fn get_first_field<'a>(
39 &'a self,
40 fq_path: &'_ str,
41 field: &'_ str,
42 ) -> Option<&'a T> {
43 self.find_best_matching(&format!("{fq_path}.{field}"))
44 }
45
46 pub(crate) fn clear(&mut self) {
48 self.matchers.clear();
49 }
50
51 fn find_best_matching(&self, full_path: &str) -> Option<&T> {
54 sub_path_iter(full_path).find_map(|path| {
55 self.matchers
56 .iter()
57 .find(|(p, _)| p == path)
58 .map(|(_, v)| v)
59 })
60 }
61}
62
63pub(crate) struct Iter<'a, T> {
65 iter: std::slice::Iter<'a, (String, T)>,
66 path: String,
67}
68
69impl<'a, T> Iter<'a, T> {
70 fn new(map: &'a PathMap<T>, path: String) -> Self {
71 Self {
72 iter: map.matchers.iter(),
73 path,
74 }
75 }
76
77 fn is_match(&self, path: &str) -> bool {
78 sub_path_iter(self.path.as_str()).any(|p| p == path)
79 }
80}
81
82impl<'a, T> std::iter::Iterator for Iter<'a, T> {
83 type Item = &'a T;
84
85 fn next(&mut self) -> Option<Self::Item> {
86 loop {
87 match self.iter.next() {
88 Some((p, v)) => {
89 if self.is_match(p) {
90 return Some(v);
91 }
92 }
93 None => return None,
94 }
95 }
96 }
97}
98
99impl<T> std::iter::FusedIterator for Iter<'_, T> {}
100
101fn sub_path_iter(full_path: &str) -> impl Iterator<Item = &str> {
109 iter::once(full_path)
111 .chain(suffixes(full_path))
113 .chain(prefixes(full_path))
115 .chain(iter::once("."))
117}
118
119fn prefixes(fq_path: &str) -> impl Iterator<Item = &str> {
124 std::iter::successors(Some(fq_path), |path| {
125 #[allow(unknown_lints, clippy::manual_split_once)]
126 path.rsplitn(2, '.').nth(1).filter(|path| !path.is_empty())
127 })
128 .skip(1)
129}
130
131fn suffixes(fq_path: &str) -> impl Iterator<Item = &str> {
136 std::iter::successors(Some(fq_path), |path| {
137 #[allow(unknown_lints, clippy::manual_split_once)]
138 path.splitn(2, '.').nth(1).filter(|path| !path.is_empty())
139 })
140 .skip(1)
141}
142
143#[cfg(test)]
144mod tests {
145
146 use super::*;
147
148 #[test]
149 fn test_prefixes() {
150 assert_eq!(
151 prefixes(".a.b.c.d").collect::<Vec<_>>(),
152 vec![".a.b.c", ".a.b", ".a"],
153 );
154 assert_eq!(prefixes(".a").count(), 0);
155 assert_eq!(prefixes(".").count(), 0);
156 }
157
158 #[test]
159 fn test_suffixes() {
160 assert_eq!(
161 suffixes(".a.b.c.d").collect::<Vec<_>>(),
162 vec!["a.b.c.d", "b.c.d", "c.d", "d"],
163 );
164 assert_eq!(suffixes(".a").collect::<Vec<_>>(), vec!["a"]);
165 assert_eq!(suffixes(".").collect::<Vec<_>>(), Vec::<&str>::new());
166 }
167
168 #[test]
169 fn test_get_matches_sub_path() {
170 let mut path_map = PathMap::default();
171
172 path_map.insert(".a.b.c.d".to_owned(), 1);
174 assert_eq!(Some(&1), path_map.get(".a.b.c.d").next());
175 assert_eq!(Some(&1), path_map.get_field(".a.b.c", "d").next());
176
177 path_map.clear();
179 path_map.insert("c.d".to_owned(), 1);
180 assert_eq!(Some(&1), path_map.get(".a.b.c.d").next());
181 assert_eq!(Some(&1), path_map.get("b.c.d").next());
182 assert_eq!(Some(&1), path_map.get_field(".a.b.c", "d").next());
183
184 path_map.clear();
186 path_map.insert(".a.b".to_owned(), 1);
187 assert_eq!(Some(&1), path_map.get(".a.b.c.d").next());
188 assert_eq!(Some(&1), path_map.get_field(".a.b.c", "d").next());
189
190 path_map.clear();
192 path_map.insert(".".to_owned(), 1);
193 assert_eq!(Some(&1), path_map.get(".a.b.c.d").next());
194 assert_eq!(Some(&1), path_map.get("b.c.d").next());
195 assert_eq!(Some(&1), path_map.get_field(".a.b.c", "d").next());
196 }
197
198 #[test]
199 fn test_get_best() {
200 let mut path_map = PathMap::default();
201
202 path_map.insert(".".to_owned(), 1);
204 assert_eq!(Some(&1), path_map.get_first(".a.b.c.d"));
205 assert_eq!(Some(&1), path_map.get_first("b.c.d"));
206 assert_eq!(Some(&1), path_map.get_first_field(".a.b.c", "d"));
207
208 path_map.insert(".a.b".to_owned(), 2);
210 assert_eq!(Some(&2), path_map.get_first(".a.b.c.d"));
211 assert_eq!(Some(&2), path_map.get_first_field(".a.b.c", "d"));
212
213 path_map.insert("c.d".to_owned(), 3);
215 assert_eq!(Some(&3), path_map.get_first(".a.b.c.d"));
216 assert_eq!(Some(&3), path_map.get_first("b.c.d"));
217 assert_eq!(Some(&3), path_map.get_first_field(".a.b.c", "d"));
218
219 path_map.insert(".a.b.c.d".to_owned(), 4);
221 assert_eq!(Some(&4), path_map.get_first(".a.b.c.d"));
222 assert_eq!(Some(&4), path_map.get_first_field(".a.b.c", "d"));
223 }
224
225 #[test]
226 fn test_get_keep_order() {
227 let mut path_map = PathMap::default();
228 path_map.insert(".".to_owned(), 1);
229 path_map.insert(".a.b".to_owned(), 2);
230 path_map.insert(".a.b.c.d".to_owned(), 3);
231
232 let mut iter = path_map.get(".a.b.c.d");
233 assert_eq!(Some(&1), iter.next());
234 assert_eq!(Some(&2), iter.next());
235 assert_eq!(Some(&3), iter.next());
236 assert_eq!(None, iter.next());
237
238 path_map.clear();
239
240 path_map.insert(".a.b.c.d".to_owned(), 1);
241 path_map.insert(".a.b".to_owned(), 2);
242 path_map.insert(".".to_owned(), 3);
243
244 let mut iter = path_map.get(".a.b.c.d");
245 assert_eq!(Some(&1), iter.next());
246 assert_eq!(Some(&2), iter.next());
247 assert_eq!(Some(&3), iter.next());
248 assert_eq!(None, iter.next());
249 }
250}