Skip to main content

zenoh_keyexpr/key_expr/format/
mod.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
15//! # Building and parsing Key Expressions
16//! A common issue in REST API is the association of meaning to sections of the URL, and respecting that API in a convenient manner.
17//! The same issue arises naturally when designing a KE space, and [`KeFormat`] was designed to help you with this,
18//! both in constructing and in parsing KEs that fit the formats you've defined.
19//!
20//! [`kedefine`](https://docs.rs/zenoh/latest/zenoh/key_expr/format/macro.kedefine.html) also allows you to define formats at compile time, allowing a more performant, but more importantly safer and more convenient use of said formats,
21//! as the [`keformat`](https://docs.rs/zenoh/latest/zenoh/key_expr/format/macro.keformat.htmll) and [`kewrite`](https://docs.rs/zenoh/latest/zenoh/key_expr/format/macro.kewrite.html) macros will be able to tell you if you're attempting to set fields of the format that do not exist.
22//!
23//! ## The format syntax
24//! KE formats are defined following a syntax that extends the [`keyexpr`] syntax. In addition to existing chunk types, KE formats support "specification" chunks.
25//! These chunks must follow the one of the following syntaxes: `${id:pattern}`, `${id:pattern#default}`, `$#{id:pattern}#`, or `$#{id:pattern#default}#`, where:
26//! - `id` is the chunk identifier: it cannot contain the `:` character, and is used to name the chunk in accessors.
27//! - `pattern` must be a valid KE (and therefore cannot contain `#`) and defines the range of values that the chunk may adopt.
28//! - `default` (optional) is used as the chunk value when formatting if the builder wasn't supplied with a value for `id`.
29//!
30//! ## Formatting
31//! To use a format to build a Key Expression, its [formatter](KeFormat::formatter) must be constructed.
32//!
33//! A formatter functions like as an `id`-value map which can be [`KeFormatter::build`] into a [`OwnedKeyExpr`] once all specs have a value.
34//!
35//! The formatter will notably prevent you from setting values for a spec that isn't included by its pattern.
36//!
37//! ## Parsing
38//! [`KeFormat`] can also be used to parse any [`keyexpr`] that intersects with it, using [`KeFormat::parse`].
39//!
40//! The parser will then assign subsections of the [`keyexpr`] to each spec, and the resulting [`Parsed`] result can then be queried
41//! for each spec's assigned value.
42//!
43//! Specs are considered greedy and evaluated left-to-right: if your format would allow ambiguous parsings, chunks will be consumed
44//! by the leftmost specs first. For example `${a:**}/-/${b:**}` parsing `hey/-/-/there` would assign `hey/-` to `a` and `there` to `b`,
45//! (even though you might have expected `a` to only consume `hey` and `b` to consume the remaining `-/there`).
46//!
47//! A good way to avoid ambiguities when working with formats that contain multiple `**` specs is to separate such specs using verbatim chunks
48//! (chunks that start with an `@`), as `**` is incapable of consuming these chunks.
49
50use alloc::{boxed::Box, string::String, vec::Vec};
51use core::{
52    convert::{TryFrom, TryInto},
53    fmt::{Debug, Display},
54    num::NonZeroU32,
55};
56
57use zenoh_result::{bail, zerror, Error, IError, ZResult};
58
59use super::{keyexpr, OwnedKeyExpr};
60
61mod support;
62pub use support::{IKeFormatStorage, Segment};
63use support::{IterativeConstructor, Spec};
64
65/// # Building and parsing Key Expressions
66/// A common issue in REST API is the association of meaning to sections of the URL, and respecting that API in a convenient manner.
67/// The same issue arises naturally when designing a KE space, and [`KeFormat`] was designed to help you with this,
68/// both in constructing and in parsing KEs that fit the formats you've defined.
69///
70/// [`kedefine`](https://docs.rs/zenoh/latest/zenoh/key_expr/format/macro.kedefine.html) also allows you to define formats at compile time, allowing a more performant, but more importantly safer and more convenient use of said formats,
71/// as the [`keformat`](https://docs.rs/zenoh/latest/zenoh/key_expr/format/macro.keformat.html) and [`kewrite`](https://docs.rs/zenoh/latest/zenoh/macro.kewrite.html) macros will be able to tell you if you're attempting to set fields of the format that do not exist.
72///
73/// ## The format syntax
74/// KE formats are defined following a syntax that extends the [`keyexpr`] syntax. In addition to existing chunk types, KE formats support "specification" chunks.
75/// These chunks must follow the one of the following syntaxes: `${id:pattern}`, `${id:pattern#default}`, `$#{id:pattern}#`, or `$#{id:pattern#default}#`, where:
76/// - `id` is the chunk identifier: it cannot contain the `:` character, and is used to name the chunk in accessors.
77/// - `pattern` must be a valid KE (and therefore cannot contain `#`) and defines the range of values that the chunk may adopt.
78/// - `default` (optional) is used as the chunk value when formatting if the builder wasn't supplied with a value for `id`.
79///
80/// ## Formatting
81/// To use a format to build a Key Expression, its [formatter](KeFormat::formatter) must be constructed.
82///
83/// A formatter functions like as an `id`-value map which can be [`KeFormatter::build`] into a [`OwnedKeyExpr`] once all specs have a value.
84///
85/// The formatter will notably prevent you from setting values for a spec that isn't included by its pattern.
86///
87/// ## Parsing
88/// [`KeFormat`] can also be used to parse any [`keyexpr`] that intersects with it, using [`KeFormat::parse`].
89///
90/// The parser will then assign subsections of the [`keyexpr`] to each spec, and the resulting [`Parsed`] result can then be queried
91/// for each spec's assigned value.
92///
93/// Specs are considered greedy and evaluated left-to-right: if your format would allow ambiguous parsings, chunks will be consumed
94/// by the leftmost specs first. For example `${a:**}/-/${b:**}` parsing `hey/-/-/there` would assign `hey/-` to `a` and `there` to `b`,
95/// (even though you might have expected `a` to only consume `hey` and `b` to consume the remaining `-/there`).
96///
97/// A good way to avoid ambiguities when working with formats that contain multiple `**` specs is to separate such specs using verbatim chunks
98/// (chunks that start with an `@`), as `**` is incapable of consuming these chunks.
99#[derive(Clone, Copy, Hash)]
100pub struct KeFormat<'s, Storage: IKeFormatStorage<'s> + 's = Vec<Segment<'s>>> {
101    /// The [`[Segment]`](Segment)s of the format.
102    storage: Storage,
103    /// The end of the format. It may be one of 3 cases:
104    /// - An empty string, in which case the format ends with the last segment.
105    /// - A keyexpr preceded by `/`.
106    /// - A keyexpr, in the case the format contains no specs.
107    suffix: &'s str,
108}
109impl<'s, Storage: IKeFormatStorage<'s>> Debug for KeFormat<'s, Storage> {
110    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111        write!(f, "{self}")
112    }
113}
114impl<'s> KeFormat<'s, Vec<Segment<'s>>> {
115    /// Construct a new [`KeFormat`], using a vector to store its state-machine and parser results.
116    pub fn new<S: AsRef<str> + ?Sized>(value: &'s S) -> ZResult<Self> {
117        value.as_ref().try_into()
118    }
119    /// Construct a new [`KeFormat], using a stack-allocated array to store its state-machine and parser results.
120    ///
121    /// `N` is simply the number of specifications in `value`. If this number of specs isn't known at compile-time, use [`KeFormat::new`] instead.
122    ///
123    /// If you know `value` at compile time, using [`kedefine`](https://docs.rs/zenoh/latest/zenoh/key_expr/format/macro.kedefine.html) instead is advised,
124    /// as it will provide more features and construct higher performance formats than this constructor.
125    pub fn noalloc_new<const N: usize>(value: &'s str) -> ZResult<KeFormat<'s, [Segment<'s>; N]>> {
126        value.try_into()
127    }
128}
129
130pub mod macro_support {
131    use super::*;
132    /// DO NOT USE THIS
133    ///
134    /// This is a support structure for [`const_new`], which is only meant to be used through the `zenoh::keformat` macro.
135    #[doc(hidden)]
136    #[derive(Clone, Copy)]
137    pub struct SegmentBuilder {
138        pub segment_start: usize,
139        pub prefix_end: usize,
140        pub spec_start: usize,
141        pub id_end: u16,
142        pub pattern_end: u16,
143        pub spec_end: usize,
144        pub segment_end: usize,
145    }
146
147    /// # Safety
148    /// DO NOT USE THIS, EVER
149    ///
150    /// This is a support function which is only meant to be used through the `zenoh::keformat` macro
151    #[doc(hidden)]
152    pub unsafe fn specs<'s>(this: &KeFormat<'s, Vec<Segment<'s>>>) -> Vec<SegmentBuilder> {
153        let segments = this.storage.segments();
154        if segments.is_empty() {
155            return Vec::new();
156        }
157        let source_start = segments[0].prefix.as_ptr() as usize;
158        segments
159            .iter()
160            .map(|segment| {
161                let segment_start = segment.prefix.as_ptr() as usize - source_start;
162                let prefix_end = segment_start + segment.prefix.len();
163                let spec_start = segment.spec.spec.as_ptr() as usize - source_start;
164                let spec_end = spec_start + segment.spec.spec.len();
165                let segment_end = spec_end + spec_start - prefix_end - 1;
166                SegmentBuilder {
167                    segment_start,
168                    prefix_end,
169                    spec_start,
170                    id_end: segment.spec.id_end,
171                    pattern_end: segment.spec.pattern_end,
172                    spec_end,
173                    segment_end,
174                }
175            })
176            .collect()
177    }
178    /// # Safety
179    /// DO NOT USE THIS, EVER
180    ///
181    /// This is a support function which is only meant to be used through the `zenoh::keformat` macro
182    #[doc(hidden)]
183    pub const unsafe fn const_new<const N: usize>(
184        source: &'static str,
185        segments: [SegmentBuilder; N],
186    ) -> KeFormat<'static, [Segment<'static>; N]> {
187        /// # Safety
188        /// `start..end` must be in-bounds for `source`, and delimit UTF-8 boundaries.
189        const unsafe fn substr(source: &'static str, start: usize, end: usize) -> &'static str {
190            // SAFETY: guaranteed by `const_new` caller and macro-generated offsets.
191            unsafe {
192                core::str::from_utf8_unchecked(core::slice::from_raw_parts(
193                    source.as_ptr().add(start),
194                    end - start,
195                ))
196            }
197        }
198        let mut storage = [Segment {
199            prefix: "",
200            spec: Spec {
201                spec: "",
202                id_end: 0,
203                pattern_end: 0,
204            },
205        }; N];
206        let mut suffix_start = 0;
207        let mut i = 0;
208        while i < N {
209            let segment = segments[i];
210            // SAFETY: macro-generated indices point to valid UTF-8 segment boundaries.
211            let prefix = unsafe { substr(source, segment.segment_start, segment.prefix_end) };
212            let spec = Spec {
213                // SAFETY: macro-generated indices point to valid UTF-8 segment boundaries.
214                spec: unsafe { substr(source, segment.spec_start, segment.spec_end) },
215                id_end: segment.id_end,
216                pattern_end: segment.pattern_end,
217            };
218            storage[i] = Segment { prefix, spec };
219            suffix_start = segment.segment_end;
220            i += 1;
221        }
222        // SAFETY: `suffix_start` is derived from macro-generated segment ends.
223        let suffix = unsafe { substr(source, suffix_start, source.len()) };
224        KeFormat { storage, suffix }
225    }
226}
227impl<'s, Storage: IKeFormatStorage<'s> + 's> KeFormat<'s, Storage> {
228    /// Constructs a new formatter for the format.
229    pub fn formatter(&'s self) -> KeFormatter<'s, Storage> {
230        KeFormatter {
231            format: self,
232            buffer: String::new(),
233            values: Storage::values_storage(&self.storage, |_| None),
234        }
235    }
236}
237impl<'s, Storage: IKeFormatStorage<'s> + 's> TryFrom<&'s String> for KeFormat<'s, Storage> {
238    type Error = Error;
239    fn try_from(value: &'s String) -> Result<Self, Self::Error> {
240        Self::try_from(value.as_str())
241    }
242}
243impl<'s, Storage: IKeFormatStorage<'s> + 's> TryFrom<&'s str> for KeFormat<'s, Storage> {
244    type Error = Error;
245    fn try_from(value: &'s str) -> Result<Self, Self::Error> {
246        let mut storage = Storage::new_constructor();
247        let mut segment_start = 0;
248        let mut i = 0;
249        let bvalue = value.as_bytes();
250        while i < bvalue.len() {
251            if bvalue[i] == b'$' {
252                let prefix_end = i;
253                i += 1;
254                let (terminator, spec_start) = match bvalue[i] {
255                    b'{' => ("}", i+ 1),
256                    b'*' => {i+= 1; continue;}
257                    b'#' if bvalue[i+1] == b'{' => ("}#", i +2),
258                    c => bail!("Invalid KeFormat: {value} contains `${}` which is not legal in KEs or formats", c as char)
259                };
260                let spec = &value[spec_start..];
261                let Some(spec_end) = spec.find(terminator) else {
262                    bail!("Invalid KeFormat: {value} contains an unterminated spec")
263                };
264                let spec = &spec[..spec_end];
265                let spec = match Spec::try_from(spec) {
266                    Ok(spec) => spec,
267                    Err(e) => {
268                        bail!(e => "Invalid KeFormat: {value} contains an invalid spec: {spec}")
269                    }
270                };
271                let segment_end = spec_end + spec_start + terminator.len();
272                if prefix_end != 0 && bvalue[prefix_end - 1] != b'/' {
273                    bail!("Invalid KeFormat: a spec in {value} is preceded a non-`/` character")
274                }
275                if !matches!(bvalue.get(segment_end), None | Some(b'/')) {
276                    bail!("Invalid KeFormat: a spec in {value} is followed by a non-`/` character")
277                }
278                keyexpr::new(spec.pattern().as_str())?; // Check that the pattern is indeed a keyexpr. We can make this more flexible in the future.
279                let prefix = &value[segment_start..prefix_end];
280                if prefix.contains('*') {
281                    bail!("Invalid KeFormat: wildcards are only allowed in specs when writing formats")
282                }
283                let segment = Segment { prefix, spec };
284                storage = match Storage::add_segment(storage, segment) {
285                    IterativeConstructor::Error(e) => {
286                        bail!("Couldn't construct KeFormat because its Storage's add_segment failed: {e}")
287                    }
288                    s => s,
289                };
290                segment_start = segment_end;
291            } else {
292                i += 1;
293            }
294        }
295        let IterativeConstructor::Complete(storage) = storage else {
296            bail!("Couldn't construct KeFormat because its Storage construction was only partial after adding the last segment.")
297        };
298        let segments = storage.segments();
299        for i in 0..(segments.len() - 1) {
300            if segments[(i + 1)..]
301                .iter()
302                .any(|s| s.spec.id() == segments[i].spec.id())
303            {
304                bail!("Invalid KeFormat: {value} contains duplicated ids")
305            }
306        }
307        let suffix = &value[segment_start..];
308        if suffix.contains('*') {
309            bail!("Invalid KeFormat: wildcards are only allowed in specs when writing formats")
310        }
311        Ok(KeFormat { storage, suffix })
312    }
313}
314
315impl<'s, Storage: IKeFormatStorage<'s> + 's> core::convert::TryFrom<&KeFormat<'s, Storage>>
316    for String
317{
318    type Error = core::fmt::Error;
319    fn try_from(value: &KeFormat<'s, Storage>) -> Result<Self, Self::Error> {
320        use core::fmt::Write;
321        let mut s = String::new();
322        for segment in value.storage.segments() {
323            s += segment.prefix;
324            write!(&mut s, "{}", segment.spec.pattern())?;
325        }
326        s += value.suffix;
327        Ok(s)
328    }
329}
330impl<'s, Storage: IKeFormatStorage<'s> + 's> core::convert::TryFrom<&KeFormat<'s, Storage>>
331    for OwnedKeyExpr
332{
333    type Error = Error;
334    fn try_from(value: &KeFormat<'s, Storage>) -> Result<Self, Self::Error> {
335        let s: String = value
336            .try_into()
337            .map_err(|e| zerror!("failed to write into String: {e}"))?;
338        OwnedKeyExpr::autocanonize(s)
339    }
340}
341
342impl<'s, Storage: IKeFormatStorage<'s> + 's> core::fmt::Display for KeFormat<'s, Storage> {
343    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
344        for segment in self.storage.segments() {
345            write!(f, "{}{}", segment.prefix, segment.spec)?;
346        }
347        write!(f, "{}", self.suffix)
348    }
349}
350
351#[derive(Debug, Clone, Copy, PartialEq, Eq)]
352struct NonMaxU32(NonZeroU32);
353impl NonMaxU32 {
354    fn new(value: u32) -> Option<Self> {
355        NonZeroU32::new(!value).map(NonMaxU32)
356    }
357    fn get(&self) -> u32 {
358        !self.0.get()
359    }
360}
361
362/// An active formatter for a [`KeFormat`].
363#[derive(Clone)]
364pub struct KeFormatter<'s, Storage: IKeFormatStorage<'s>> {
365    format: &'s KeFormat<'s, Storage>,
366    buffer: String,
367    values: Storage::ValuesStorage<Option<(u32, NonMaxU32)>>,
368}
369
370impl<'s, Storage: IKeFormatStorage<'s>> core::fmt::Debug for KeFormatter<'s, Storage> {
371    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
372        let values = self.values.as_ref();
373        let segments = self.format.storage.segments();
374        for i in 0..values.len() {
375            let Segment { prefix, spec } = segments[i];
376            let value =
377                values[i].map(|(start, end)| &self.buffer[start as usize..end.get() as usize]);
378            let id = spec.id();
379            let pattern = spec.pattern();
380            let sharp = if id.contains('}')
381                || pattern.contains('}')
382                || value.map_or_else(
383                    || spec.default().is_some_and(|v| v.contains('}')),
384                    |v| v.contains('}'),
385                ) {
386                "#"
387            } else {
388                ""
389            };
390            write!(f, "{prefix}${sharp}{{{id}:{pattern}")?;
391            if let Some(value) = value {
392                write!(f, "={value}")?
393            } else if let Some(default) = spec.default() {
394                write!(f, "#{default}")?
395            };
396            write!(f, "}}{sharp}")?;
397        }
398        write!(f, "{}", self.format.suffix)
399    }
400}
401
402impl<'s, Storage: IKeFormatStorage<'s>> TryFrom<&KeFormatter<'s, Storage>> for OwnedKeyExpr {
403    type Error = Error;
404
405    fn try_from(value: &KeFormatter<'s, Storage>) -> Result<Self, Self::Error> {
406        let values = value.values.as_ref();
407        let segments = value.format.storage.segments();
408        let mut len = value.format.suffix.len();
409        for i in 0..values.len() {
410            len += segments[i].prefix.len()
411                + if let Some((start, end)) = values[i] {
412                    (end.get() - start) as usize
413                } else if let Some(default) = segments[i].spec.default() {
414                    default.len()
415                } else {
416                    bail!("Missing field `{}` in {value:?}", segments[i].spec.id())
417                };
418        }
419        let mut ans = String::with_capacity(len);
420        let mut concatenate = |s: &str| {
421            let skip_slash = matches!(ans.as_bytes().last(), None | Some(b'/'))
422                && s.as_bytes().first() == Some(&b'/');
423            ans += &s[skip_slash as usize..];
424        };
425        for i in 0..values.len() {
426            concatenate(segments[i].prefix);
427            if let Some((start, end)) = values[i] {
428                let end = end.get();
429                concatenate(&value.buffer[start as usize..end as usize])
430            } else if let Some(default) = segments[i].spec.default() {
431                concatenate(default)
432            } else {
433                // SAFETY: upheld by the surrounding invariants and prior validation.
434                unsafe { core::hint::unreachable_unchecked() }
435            };
436        }
437        concatenate(value.format.suffix);
438        if ans.ends_with('/') {
439            ans.pop();
440        }
441        OwnedKeyExpr::autocanonize(ans)
442    }
443}
444impl<'s, Storage: IKeFormatStorage<'s>> TryFrom<&mut KeFormatter<'s, Storage>> for OwnedKeyExpr {
445    type Error = Error;
446
447    fn try_from(value: &mut KeFormatter<'s, Storage>) -> Result<Self, Self::Error> {
448        (&*value).try_into()
449    }
450}
451
452#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
453pub enum FormatSetError {
454    InvalidId,
455    PatternNotMatched,
456}
457impl core::fmt::Display for FormatSetError {
458    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
459        write!(f, "{self:?}")
460    }
461}
462impl IError for FormatSetError {}
463impl<'s, Storage: IKeFormatStorage<'s>> KeFormatter<'s, Storage> {
464    /// Access the formatter's format
465    pub fn format(&self) -> &KeFormat<'s, Storage> {
466        self.format
467    }
468
469    /// Clear the formatter of previously set values, without deallocating its internal formatting buffer.
470    pub fn clear(&mut self) -> &mut Self {
471        self.buffer.clear();
472        for value in self.values.as_mut() {
473            *value = None
474        }
475        self
476    }
477
478    /// Build a key-expression according to the format and the currently set values.
479    ///
480    /// This doesn't clear the formatter of already set values, allowing to reuse the builder and only
481    /// change a subset of its properties before building a new key-expression again.
482    pub fn build(&self) -> ZResult<OwnedKeyExpr> {
483        self.try_into()
484    }
485    /// Access the current value for `id`.
486    pub fn get(&self, id: &str) -> Option<&str> {
487        let segments = self.format.storage.segments();
488        segments
489            .iter()
490            .position(|s| s.spec.id() == id)
491            .and_then(|i| {
492                self.values.as_ref()[i]
493                    .map(|(start, end)| &self.buffer[start as usize..end.get() as usize])
494            })
495    }
496    /// Set a new value for `id` using `S`'s [`Display`] formatting.
497    ///
498    /// # Errors
499    /// If the result of `format!("{value}")` is neither:
500    /// - A valid key expression that is included by the pattern for `id`
501    /// - An empty string, on the condition that `id`'s pattern is `**`
502    pub fn set<S: Display>(&mut self, id: &str, value: S) -> Result<&mut Self, FormatSetError> {
503        use core::fmt::Write;
504        let segments = self.format.storage.segments();
505        let Some(i) = segments.iter().position(|s| s.spec.id() == id) else {
506            return Err(FormatSetError::InvalidId);
507        };
508        let values = self.values.as_mut();
509        if let Some((start, end)) = values[i].take() {
510            let end = end.get();
511            let shift = end - start;
512            self.buffer.replace_range(start as usize..end as usize, "");
513            for (s, e) in values.iter_mut().flatten() {
514                if *s < start {
515                    continue;
516                }
517                *s -= shift;
518                *e = NonMaxU32::new(e.get() - shift).unwrap()
519            }
520        }
521        let pattern = segments[i].spec.pattern();
522        let start = self.buffer.len();
523        write!(&mut self.buffer, "{value}").unwrap(); // Writing on `&mut String` should be infallible.
524        let mut set_value = || {
525            let end = self.buffer.len();
526            if start == end {
527                if !pattern.is_double_wild() {
528                    return Err(());
529                }
530            } else {
531                let Ok(ke) = keyexpr::new(&self.buffer[start..end]) else {
532                    return Err(());
533                };
534                if !pattern.includes(ke) {
535                    return Err(());
536                }
537            }
538
539            values[i] = Some((
540                start as u32,
541                NonMaxU32::new(end.try_into().map_err(|_| ())?).ok_or(())?,
542            ));
543            Ok(())
544        };
545        match set_value() {
546            Ok(()) => Ok(self),
547            Err(()) => {
548                self.buffer.truncate(start);
549                Err(FormatSetError::PatternNotMatched)
550            }
551        }
552    }
553}
554
555/// A [`KeFormat`] that owns its format-string.
556pub struct OwnedKeFormat<Storage: IKeFormatStorage<'static> + 'static = Vec<Segment<'static>>> {
557    _owner: Box<str>,
558    format: KeFormat<'static, Storage>,
559}
560impl<Storage: IKeFormatStorage<'static> + 'static> core::ops::Deref for OwnedKeFormat<Storage> {
561    type Target = KeFormat<'static, Storage>;
562    fn deref(&self) -> &Self::Target {
563        &self.format
564    }
565}
566impl<Storage: IKeFormatStorage<'static> + 'static> TryFrom<Box<str>> for OwnedKeFormat<Storage> {
567    type Error = <KeFormat<'static, Storage> as TryFrom<&'static str>>::Error;
568    fn try_from(value: Box<str>) -> Result<Self, Self::Error> {
569        let owner = value;
570        // SAFETY: upheld by the surrounding invariants and prior validation.
571        let format: KeFormat<'static, Storage> = unsafe {
572            // This is safe because
573            core::mem::transmute::<&str, &'static str>(&owner)
574        }
575        .try_into()?;
576        Ok(Self {
577            _owner: owner,
578            format,
579        })
580    }
581}
582impl<Storage: IKeFormatStorage<'static> + 'static> TryFrom<String> for OwnedKeFormat<Storage> {
583    type Error = <KeFormat<'static, Storage> as TryFrom<&'static str>>::Error;
584    fn try_from(value: String) -> Result<Self, Self::Error> {
585        value.into_boxed_str().try_into()
586    }
587}
588impl<Storage: IKeFormatStorage<'static> + 'static> core::str::FromStr for OwnedKeFormat<Storage> {
589    type Err = <KeFormat<'static, Storage> as TryFrom<&'static str>>::Error;
590    fn from_str(s: &str) -> Result<Self, Self::Err> {
591        Box::<str>::from(s).try_into()
592    }
593}
594impl<'s, S1: IKeFormatStorage<'s> + 's, S2: IKeFormatStorage<'s> + 's> PartialEq<KeFormat<'s, S2>>
595    for KeFormat<'s, S1>
596{
597    fn eq(&self, other: &KeFormat<'s, S2>) -> bool {
598        self.suffix == other.suffix && self.storage.segments() == other.storage.segments()
599    }
600}
601
602#[test]
603fn formatting() {
604    let format = KeFormat::new("a/${a:*}/b/$#{b:**}#/c").unwrap();
605    assert_eq!(format.storage[0].prefix, "a/");
606    assert_eq!(format.storage[0].spec.id(), "a");
607    assert_eq!(format.storage[0].spec.pattern(), "*");
608    assert_eq!(format.storage[1].prefix, "/b/");
609    assert_eq!(format.storage[1].spec.id(), "b");
610    assert_eq!(format.storage[1].spec.pattern(), "**");
611    assert_eq!(format.suffix, "/c");
612    let ke: OwnedKeyExpr = format
613        .formatter()
614        .set("a", 1)
615        .unwrap()
616        .set("b", "hi/there")
617        .unwrap()
618        .try_into()
619        .unwrap();
620    assert_eq!(ke.as_str(), "a/1/b/hi/there/c");
621    let ke: OwnedKeyExpr = format
622        .formatter()
623        .set("a", 1)
624        .unwrap()
625        .set("b", "")
626        .unwrap()
627        .try_into()
628        .unwrap();
629    assert_eq!(ke.as_str(), "a/1/b/c");
630}
631
632mod parsing;
633pub use parsing::{Iter, Parsed};