Skip to main content

zenoh_keyexpr/key_expr/format/
parsing.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15use 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    /// Access the `id` element.
36    ///
37    /// The returned string is guaranteed to either be an empty string or a valid key expression.
38    ///
39    /// # Errors
40    /// If `id` is not part of `self`'s specs.
41    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    /// The raw values for each spec, in left-to-right order.
54    pub fn values(&self) -> &[Option<&'s keyexpr>] {
55        self.results.as_ref()
56    }
57    /// Iterates over id-value pairs.
58    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    /// Parses `target` according to `self`. The returned [`Parsed`] object can be used to extract the values of the fields in `self` from `target`.
130    ///
131    /// Parsing is greedy and done left-to-right. Please refer to [`KeFormat`]'s documentation for more details.
132    ///
133    /// # Errors
134    /// If `target` does not intersect with `self`, an error is returned.
135    pub fn parse(&'s self, target: &'s keyexpr) -> ZResult<Parsed<'s, Storage>> {
136        let segments = self.storage.segments();
137        if segments.is_empty()
138            // SAFETY: upheld by the surrounding invariants and prior validation.
139            && !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                    // SAFETY: upheld by the surrounding invariants and prior validation.
151                    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            // if target is empty
188            let Some(target) = target else {
189                // this segment only matches if the pattern is `**` and the prefix is empty (since it cannot be `**`)
190                if prefix.is_none() && pattern.is_double_wild() {
191                    *result = None;
192                    // the next segments still have to be checked to respect the same condition
193                    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 the prefix can be compressed to empty,
230            if prefix.is_none() {
231                try_intersect!(pattern, result, target, segments, results);
232            }
233            // iterate through as many splits as `prefix` could possibly consume.
234            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}