zenoh_keyexpr/key_expr/format/
parsing.rs1use zenoh_result::{bail, ZResult};
16
17use super::{IKeFormatStorage, KeFormat, Segment};
18use crate::key_expr::keyexpr;
19
20pub struct Parsed<'s, Storage: IKeFormatStorage<'s>> {
21 format: &'s KeFormat<'s, Storage>,
22 results: Storage::ValuesStorage<Option<&'s keyexpr>>,
23}
24
25impl<'s, Storage: IKeFormatStorage<'s>> core::fmt::Debug for Parsed<'s, Storage> {
26 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
27 f.debug_struct("Parsed")
28 .field("format", &self.format)
29 .field("results_len", &self.results.as_ref().len())
30 .finish()
31 }
32}
33
34impl<'s, Storage: IKeFormatStorage<'s>> Parsed<'s, Storage> {
35 pub fn get(&self, id: &str) -> ZResult<&'s str> {
42 let Some(i) = self
43 .format
44 .storage
45 .segments()
46 .iter()
47 .position(|s| s.spec.id() == id)
48 else {
49 bail!("{} has no {id} field", self.format)
50 };
51 Ok(self.results.as_ref()[i].map_or("", keyexpr::as_str))
52 }
53 pub fn values(&self) -> &[Option<&'s keyexpr>] {
55 self.results.as_ref()
56 }
57 pub fn iter(&'s self) -> Iter<'s, Storage> {
59 self.into_iter()
60 }
61}
62
63impl<'s, Storage: IKeFormatStorage<'s>> IntoIterator for &'s Parsed<'s, Storage> {
64 type Item = <Self::IntoIter as Iterator>::Item;
65 type IntoIter = Iter<'s, Storage>;
66 fn into_iter(self) -> Self::IntoIter {
67 Iter {
68 parsed: self,
69 start: 0,
70 end: self.format.storage.segments().len(),
71 }
72 }
73}
74pub struct Iter<'s, Storage: IKeFormatStorage<'s>> {
75 parsed: &'s Parsed<'s, Storage>,
76 start: usize,
77 end: usize,
78}
79
80impl<'s, Storage: IKeFormatStorage<'s>> core::fmt::Debug for Iter<'s, Storage> {
81 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82 f.debug_struct("Iter")
83 .field("parsed", &"..")
84 .field("start", &self.start)
85 .field("end", &self.end)
86 .finish()
87 }
88}
89impl<'s, Storage: IKeFormatStorage<'s>> Iterator for Iter<'s, Storage> {
90 type Item = (&'s str, Option<&'s keyexpr>);
91 fn next(&mut self) -> Option<Self::Item> {
92 if self.start < self.end {
93 let id = self.parsed.format.storage.segments()[self.start].spec.id();
94 let ke = self.parsed.results.as_ref()[self.start];
95 self.start += 1;
96 Some((id, ke))
97 } else {
98 None
99 }
100 }
101 fn nth(&mut self, n: usize) -> Option<Self::Item> {
102 self.start += n;
103 self.next()
104 }
105 fn size_hint(&self) -> (usize, Option<usize>) {
106 let result = self.end - self.start;
107 (result, Some(result))
108 }
109}
110impl<'s, Storage: IKeFormatStorage<'s>> ExactSizeIterator for Iter<'s, Storage> {
111 fn len(&self) -> usize {
112 self.end - self.start
113 }
114}
115impl<'s, Storage: IKeFormatStorage<'s>> DoubleEndedIterator for Iter<'s, Storage> {
116 fn next_back(&mut self) -> Option<Self::Item> {
117 if self.start < self.end {
118 self.end -= 1;
119 let id = self.parsed.format.storage.segments()[self.end].spec.id();
120 let ke = self.parsed.results.as_ref()[self.end];
121 Some((id, ke))
122 } else {
123 None
124 }
125 }
126}
127
128impl<'s, Storage: IKeFormatStorage<'s> + 's> KeFormat<'s, Storage> {
129 pub fn parse(&'s self, target: &'s keyexpr) -> ZResult<Parsed<'s, Storage>> {
136 let segments = self.storage.segments();
137 if segments.is_empty()
138 && !target.intersects(unsafe { keyexpr::from_str_unchecked(self.suffix) })
140 {
141 bail!("{target} does not intersect with {self}")
142 }
143 let mut results = self.storage.values_storage(|_| None);
144 let results_mut = results.as_mut();
145 debug_assert_eq!(segments.len(), results_mut.len());
146 let found = 'a: {
147 match self.suffix.as_bytes() {
148 [] => do_parse(Some(target), segments, results_mut),
149 [b'/', suffix @ ..] => {
150 let suffix = unsafe { keyexpr::from_slice_unchecked(suffix) };
152 for (target, candidate) in target.iter_splits_rtl() {
153 if suffix.intersects(candidate)
154 && do_parse(Some(target), segments, results_mut)
155 {
156 break 'a true;
157 }
158 }
159 suffix.intersects(target) && do_parse(None, segments, results_mut)
160 }
161 _ => {
162 unreachable!();
163 }
164 }
165 };
166 if found {
167 Ok(Parsed {
168 format: self,
169 results,
170 })
171 } else {
172 bail!("{target} does not intersect with {self}")
173 }
174 }
175}
176
177fn do_parse<'a>(
178 target: Option<&'a keyexpr>,
179 segments: &[Segment],
180 results: &mut [Option<&'a keyexpr>],
181) -> bool {
182 match (segments, results) {
183 ([], []) => target.map_or(true, keyexpr::is_double_wild),
184 ([segment, segments @ ..], [result, results @ ..]) => {
185 let prefix = segment.prefix();
186 let pattern = segment.pattern();
187 let Some(target) = target else {
189 if prefix.is_none() && pattern.is_double_wild() {
191 *result = None;
192 return !segments.iter().zip(results).any(|(segment, result)| {
194 *result = None;
195 segment.prefix().is_some() || !segment.pattern().is_double_wild()
196 });
197 } else {
198 return false;
199 }
200 };
201 macro_rules! try_intersect {
202 ($pattern: expr, $result: expr, $target: expr, $segments: expr, $results: expr) => {{
203 let target = $target;
204 let segments = $segments;
205 if $pattern.intersects(target)
206 && do_parse(
207 target.is_double_wild().then_some(target),
208 segments,
209 $results,
210 )
211 {
212 *$result = Some(target);
213 return true;
214 }
215 for (candidate, target) in target.iter_splits_rtl() {
216 if $pattern.intersects(candidate)
217 && do_parse(Some(target), segments, $results)
218 {
219 *result = Some(candidate);
220 return true;
221 }
222 }
223 if $pattern.is_double_wild() && do_parse(Some(target), segments, $results) {
224 *$result = None;
225 return true;
226 }
227 }};
228 }
229 if prefix.is_none() {
231 try_intersect!(pattern, result, target, segments, results);
232 }
233 for (candidate, target) in target.iter_splits_ltr().take(match prefix {
235 None => 1,
236 Some(prefix) => (prefix.bytes().filter(|&c| c == b'/').count() + 1) * 3,
237 }) {
238 if prefix.map_or(candidate.is_double_wild(), |prefix| {
239 prefix.intersects(candidate)
240 }) {
241 try_intersect!(pattern, result, target, segments, results);
242 }
243 }
244 pattern.is_double_wild()
245 && prefix.is_some_and(|prefix| prefix.intersects(target))
246 && do_parse(None, segments, results)
247 }
248 _ => unreachable!(),
249 }
250}
251
252#[test]
253fn parsing() {
254 use core::convert::TryFrom;
255
256 use crate::key_expr::OwnedKeyExpr;
257 for a_spec in ["${a:*}", "a/${a:*}"] {
258 for b_spec in ["b/${b:**}", "${b:**}"] {
259 let specs = [a_spec, b_spec, "c"];
260 for spec in [2, 3] {
261 let spec = specs[..spec].join("/");
262 let format: KeFormat<[Segment; 2]> = KeFormat::noalloc_new(&spec).unwrap();
263 let mut formatter = format.formatter();
264 for a_val in ["hi"] {
265 formatter.set("a", a_val).unwrap();
266 for b_val in ["hello", "hello/there", ""] {
267 formatter.set("b", b_val).unwrap();
268 let ke = OwnedKeyExpr::try_from(&formatter).unwrap();
269 let parsed = format.parse(&ke).unwrap();
270 assert_eq!(parsed.get("a").unwrap(), a_val);
271 assert_eq!(parsed.get("b").unwrap(), b_val);
272 }
273 }
274 }
275 }
276 }
277 KeFormat::new("**/${a:**}/${b:**}/**").unwrap_err();
278 let format = KeFormat::new("${a:**}/${b:**}").unwrap();
279 assert_eq!(
280 format
281 .parse(keyexpr::new("a/b/c").unwrap())
282 .unwrap()
283 .get("a")
284 .unwrap(),
285 "a/b/c"
286 );
287 assert_eq!(
288 format
289 .parse(keyexpr::new("**").unwrap())
290 .unwrap()
291 .get("a")
292 .unwrap(),
293 "**"
294 );
295 assert_eq!(
296 format
297 .parse(keyexpr::new("**").unwrap())
298 .unwrap()
299 .get("b")
300 .unwrap(),
301 "**"
302 );
303 let format = KeFormat::new("hi/${a:there}/${b:**}").unwrap();
304 assert_eq!(
305 format
306 .parse(keyexpr::new("hi/**").unwrap())
307 .unwrap()
308 .get("a")
309 .unwrap(),
310 "**"
311 );
312 assert_eq!(
313 format
314 .parse(keyexpr::new("hi/**").unwrap())
315 .unwrap()
316 .get("b")
317 .unwrap(),
318 "**"
319 );
320 let format = KeFormat::new("hi/${a:there}/@/${b:**}").unwrap();
321 assert_eq!(
322 format
323 .parse(keyexpr::new("hi/**/@").unwrap())
324 .unwrap()
325 .get("a")
326 .unwrap(),
327 "**"
328 );
329 assert_eq!(
330 format
331 .parse(keyexpr::new("hi/**/@").unwrap())
332 .unwrap()
333 .get("b")
334 .unwrap(),
335 ""
336 );
337}