Skip to main content

zenoh_keyexpr/key_expr/format/
support.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 alloc::{boxed::Box, vec::Vec};
16use core::convert::{TryFrom, TryInto};
17
18use zenoh_result::{bail, zerror, Error};
19
20use crate::key_expr::keyexpr;
21
22#[derive(Clone, Copy, PartialEq, Eq, Hash)]
23pub(crate) struct Spec<'a> {
24    pub(crate) spec: &'a str,
25    pub(crate) id_end: u16,
26    pub(crate) pattern_end: u16,
27}
28impl<'a> TryFrom<&'a str> for Spec<'a> {
29    type Error = Error;
30    fn try_from(spec: &'a str) -> Result<Self, Self::Error> {
31        let Some(id_end) = spec.find(':') else {
32            bail!("Spec {spec} didn't contain `:`")
33        };
34        let pattern_start = id_end + 1;
35        let pattern_end = spec[pattern_start..].find('#').unwrap_or(u16::MAX as usize);
36        if pattern_start < spec.len() {
37            let Ok(id_end) = id_end.try_into() else {
38                bail!("Spec {spec} contains an id longer than {}", u16::MAX)
39            };
40            if pattern_end > u16::MAX as usize {
41                bail!("Spec {spec} contains a pattern longer than {}", u16::MAX)
42            }
43            Ok(Self {
44                spec,
45                id_end,
46                pattern_end: pattern_end as u16,
47            })
48        } else {
49            bail!("Spec {spec} has an empty pattern")
50        }
51    }
52}
53impl Spec<'_> {
54    pub fn id(&self) -> &str {
55        &self.spec[..self.id_end as usize]
56    }
57    pub fn pattern(&self) -> &keyexpr {
58        // SAFETY: upheld by the surrounding invariants and prior validation.
59        unsafe {
60            keyexpr::from_str_unchecked(if self.pattern_end != u16::MAX {
61                &self.spec[(self.id_end + 1) as usize..self.pattern_end as usize]
62            } else {
63                &self.spec[(self.id_end + 1) as usize..]
64            })
65        }
66    }
67    pub fn default(&self) -> Option<&keyexpr> {
68        let pattern_end = self.pattern_end as usize;
69        (self.spec.len() > pattern_end)
70            // SAFETY: upheld by the surrounding invariants and prior validation.
71            .then(|| unsafe { keyexpr::from_str_unchecked(&self.spec[(pattern_end + 1)..]) })
72    }
73}
74impl core::fmt::Debug for Spec<'_> {
75    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76        core::fmt::Display::fmt(self, f)
77    }
78}
79impl core::fmt::Display for Spec<'_> {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        let spec = self.spec;
82        if spec.contains('}') {
83            write!(f, "$#{{{spec}}}#")
84        } else {
85            write!(f, "${{{spec}}}")
86        }
87    }
88}
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90pub struct Segment<'a> {
91    /// What precedes a spec in a [`KeFormat`].
92    /// It may be:
93    /// - empty if the spec is the first thing in the format.
94    /// - `/` if the spec comes right after another spec.
95    /// - a valid keyexpr followed by `/` if the spec comes after a keyexpr.
96    pub(crate) prefix: &'a str,
97    pub(crate) spec: Spec<'a>,
98}
99impl Segment<'_> {
100    pub fn prefix(&self) -> Option<&keyexpr> {
101        match self.prefix {
102            "" | "/" => None,
103            // SAFETY: upheld by the surrounding invariants and prior validation.
104            _ => Some(unsafe {
105                keyexpr::from_str_unchecked(trim_suffix_slash(trim_prefix_slash(self.prefix)))
106            }),
107        }
108    }
109    pub fn id(&self) -> &str {
110        self.spec.id()
111    }
112    pub fn pattern(&self) -> &keyexpr {
113        self.spec.pattern()
114    }
115    pub fn default(&self) -> Option<&keyexpr> {
116        self.spec.default()
117    }
118}
119
120#[derive(Debug)]
121pub enum IterativeConstructor<Complete, Partial, Error> {
122    Complete(Complete),
123    Partial(Partial),
124    Error(Error),
125}
126pub trait IKeFormatStorage<'s>: Sized {
127    type PartialConstruct;
128    type ConstructionError: core::fmt::Display;
129    fn new_constructor(
130    ) -> IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError>;
131    fn add_segment(
132        constructor: IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError>,
133        segment: Segment<'s>,
134    ) -> IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError>;
135    fn segments(&self) -> &[Segment<'s>];
136    fn segments_mut(&mut self) -> &mut [Segment<'s>];
137    fn segment(&self, id: &str) -> Option<&Segment<'s>> {
138        self.segments().iter().find(|s| s.spec.id() == id)
139    }
140    fn segment_mut(&mut self, id: &str) -> Option<&mut Segment<'s>> {
141        self.segments_mut().iter_mut().find(|s| s.spec.id() == id)
142    }
143    type ValuesStorage<T>: AsMut<[T]> + AsRef<[T]>;
144    fn values_storage<T, F: FnMut(usize) -> T>(&self, f: F) -> Self::ValuesStorage<T>;
145}
146
147impl<'s, const N: usize> IKeFormatStorage<'s> for [Segment<'s>; N] {
148    type PartialConstruct = ([core::mem::MaybeUninit<Segment<'s>>; N], u16);
149    type ConstructionError = Error;
150    fn new_constructor(
151    ) -> IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError> {
152        if N > u16::MAX as usize {
153            IterativeConstructor::Error(
154                zerror!(
155                    "[Segments; {N}] unsupported because {N} is too big (max: {}).",
156                    u16::MAX
157                )
158                .into(),
159            )
160        } else {
161            IterativeConstructor::Partial(([core::mem::MaybeUninit::uninit(); N], 0))
162        }
163    }
164    fn add_segment(
165        constructor: IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError>,
166        segment: Segment<'s>,
167    ) -> IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError> {
168        match constructor {
169            IterativeConstructor::Complete(_) => IterativeConstructor::Error(
170                zerror!("Attempted to add more than {N} segments to [Segment<'s>; {N}]").into(),
171            ),
172            IterativeConstructor::Partial((mut this, n)) => {
173                let mut n = n as usize;
174                this[n] = core::mem::MaybeUninit::new(segment);
175                n += 1;
176                if n == N {
177                    // SAFETY: upheld by the surrounding invariants and prior validation.
178                    IterativeConstructor::Complete(this.map(|e| unsafe { e.assume_init() }))
179                } else {
180                    IterativeConstructor::Partial((this, n as u16))
181                }
182            }
183            IterativeConstructor::Error(e) => IterativeConstructor::Error(e),
184        }
185    }
186
187    fn segments(&self) -> &[Segment<'s>] {
188        self
189    }
190    fn segments_mut(&mut self) -> &mut [Segment<'s>] {
191        self
192    }
193
194    type ValuesStorage<T> = [T; N];
195    fn values_storage<T, F: FnMut(usize) -> T>(&self, mut f: F) -> Self::ValuesStorage<T> {
196        let mut values = PartialSlice::new();
197        for i in 0..N {
198            values.push(f(i));
199        }
200        match values.try_into() {
201            Ok(v) => v,
202            Err(_) => unreachable!(),
203        }
204    }
205}
206struct PartialSlice<T, const N: usize> {
207    buffer: [core::mem::MaybeUninit<T>; N],
208    n: u16,
209}
210
211impl<T, const N: usize> PartialSlice<T, N> {
212    fn new() -> Self {
213        Self {
214            buffer: [(); N].map(|_| core::mem::MaybeUninit::uninit()),
215            n: 0,
216        }
217    }
218    fn push(&mut self, value: T) {
219        self.buffer[self.n as usize] = core::mem::MaybeUninit::new(value);
220        self.n += 1;
221    }
222}
223impl<T, const N: usize> TryFrom<PartialSlice<T, N>> for [T; N] {
224    type Error = PartialSlice<T, N>;
225    fn try_from(value: PartialSlice<T, N>) -> Result<Self, Self::Error> {
226        // SAFETY: upheld by the surrounding invariants and prior validation.
227        let buffer = unsafe { core::ptr::read(&value.buffer) };
228        if value.n as usize == N {
229            core::mem::forget(value);
230            // SAFETY: upheld by the surrounding invariants and prior validation.
231            Ok(buffer.map(|v| unsafe { v.assume_init() }))
232        } else {
233            Err(value)
234        }
235    }
236}
237impl<T, const N: usize> Drop for PartialSlice<T, N> {
238    fn drop(&mut self) {
239        for i in 0..self.n as usize {
240            // SAFETY: upheld by the surrounding invariants and prior validation.
241            unsafe { core::mem::MaybeUninit::assume_init_drop(&mut self.buffer[i]) }
242        }
243    }
244}
245
246impl<'s> IKeFormatStorage<'s> for Vec<Segment<'s>> {
247    type PartialConstruct = core::convert::Infallible;
248    type ConstructionError = core::convert::Infallible;
249    fn new_constructor(
250    ) -> IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError> {
251        IterativeConstructor::Complete(Self::new())
252    }
253    fn add_segment(
254        constructor: IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError>,
255        segment: Segment<'s>,
256    ) -> IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError> {
257        // NOTE(fuzzypixelz): Rust 1.82.0 can detect that this pattern is irrefutable but that's not
258        // necessarily the case for prior versions. Thus we silence this lint to keep the MSRV minimal.
259        #[allow(irrefutable_let_patterns)]
260        let IterativeConstructor::Complete(mut this) = constructor
261        else {
262            // SAFETY: upheld by the surrounding invariants and prior validation.
263            unsafe { core::hint::unreachable_unchecked() }
264        };
265        this.push(segment);
266        IterativeConstructor::Complete(this)
267    }
268
269    fn segments(&self) -> &[Segment<'s>] {
270        self
271    }
272    fn segments_mut(&mut self) -> &mut [Segment<'s>] {
273        self
274    }
275
276    type ValuesStorage<T> = Box<[T]>;
277    fn values_storage<T, F: FnMut(usize) -> T>(&self, mut f: F) -> Self::ValuesStorage<T> {
278        let mut ans = Vec::with_capacity(self.len());
279        for i in 0..self.len() {
280            ans.push(f(i))
281        }
282        ans.into()
283    }
284}
285
286/// Trim the prefix slash from a target string if it has one.
287/// # Safety
288/// `target` is assumed to be a valid `keyexpr` except for the leading slash.
289pub(crate) fn trim_prefix_slash(target: &str) -> &str {
290    &target[matches!(target.as_bytes().first(), Some(b'/')) as usize..]
291}
292pub(crate) fn trim_suffix_slash(target: &str) -> &str {
293    &target[..(target.len() - matches!(target.as_bytes().last(), Some(b'/')) as usize)]
294}