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        const unsafe fn substr(source: &'static str, start: usize, end: usize) -> &'static str {
188            core::str::from_utf8_unchecked(core::slice::from_raw_parts(
189                source.as_ptr().add(start),
190                end - start,
191            ))
192        }
193        let mut storage = [Segment {
194            prefix: "",
195            spec: Spec {
196                spec: "",
197                id_end: 0,
198                pattern_end: 0,
199            },
200        }; N];
201        let mut suffix_start = 0;
202        let mut i = 0;
203        while i < N {
204            let segment = segments[i];
205            let prefix = substr(source, segment.segment_start, segment.prefix_end);
206            let spec = Spec {
207                spec: substr(source, segment.spec_start, segment.spec_end),
208                id_end: segment.id_end,
209                pattern_end: segment.pattern_end,
210            };
211            storage[i] = Segment { prefix, spec };
212            suffix_start = segment.segment_end;
213            i += 1;
214        }
215        let suffix = substr(source, suffix_start, source.len());
216        KeFormat { storage, suffix }
217    }
218}
219impl<'s, Storage: IKeFormatStorage<'s> + 's> KeFormat<'s, Storage> {
220    /// Constructs a new formatter for the format.
221    pub fn formatter(&'s self) -> KeFormatter<'s, Storage> {
222        KeFormatter {
223            format: self,
224            buffer: String::new(),
225            values: Storage::values_storage(&self.storage, |_| None),
226        }
227    }
228}
229impl<'s, Storage: IKeFormatStorage<'s> + 's> TryFrom<&'s String> for KeFormat<'s, Storage> {
230    type Error = Error;
231    fn try_from(value: &'s String) -> Result<Self, Self::Error> {
232        Self::try_from(value.as_str())
233    }
234}
235impl<'s, Storage: IKeFormatStorage<'s> + 's> TryFrom<&'s str> for KeFormat<'s, Storage> {
236    type Error = Error;
237    fn try_from(value: &'s str) -> Result<Self, Self::Error> {
238        let mut storage = Storage::new_constructor();
239        let mut segment_start = 0;
240        let mut i = 0;
241        let bvalue = value.as_bytes();
242        while i < bvalue.len() {
243            if bvalue[i] == b'$' {
244                let prefix_end = i;
245                i += 1;
246                let (terminator, spec_start) = match bvalue[i] {
247                    b'{' => ("}", i+ 1),
248                    b'*' => {i+= 1; continue;}
249                    b'#' if bvalue[i+1] == b'{' => ("}#", i +2),
250                    c => bail!("Invalid KeFormat: {value} contains `${}` which is not legal in KEs or formats", c as char)
251                };
252                let spec = &value[spec_start..];
253                let Some(spec_end) = spec.find(terminator) else {
254                    bail!("Invalid KeFormat: {value} contains an unterminated spec")
255                };
256                let spec = &spec[..spec_end];
257                let spec = match Spec::try_from(spec) {
258                    Ok(spec) => spec,
259                    Err(e) => {
260                        bail!(e => "Invalid KeFormat: {value} contains an invalid spec: {spec}")
261                    }
262                };
263                let segment_end = spec_end + spec_start + terminator.len();
264                if prefix_end != 0 && bvalue[prefix_end - 1] != b'/' {
265                    bail!("Invalid KeFormat: a spec in {value} is preceded a non-`/` character")
266                }
267                if !matches!(bvalue.get(segment_end), None | Some(b'/')) {
268                    bail!("Invalid KeFormat: a spec in {value} is followed by a non-`/` character")
269                }
270                keyexpr::new(spec.pattern().as_str())?; // Check that the pattern is indeed a keyexpr. We can make this more flexible in the future.
271                let prefix = &value[segment_start..prefix_end];
272                if prefix.contains('*') {
273                    bail!("Invalid KeFormat: wildcards are only allowed in specs when writing formats")
274                }
275                let segment = Segment { prefix, spec };
276                storage = match Storage::add_segment(storage, segment) {
277                    IterativeConstructor::Error(e) => {
278                        bail!("Couldn't construct KeFormat because its Storage's add_segment failed: {e}")
279                    }
280                    s => s,
281                };
282                segment_start = segment_end;
283            } else {
284                i += 1;
285            }
286        }
287        let IterativeConstructor::Complete(storage) = storage else {
288            bail!("Couldn't construct KeFormat because its Storage construction was only partial after adding the last segment.")
289        };
290        let segments = storage.segments();
291        for i in 0..(segments.len() - 1) {
292            if segments[(i + 1)..]
293                .iter()
294                .any(|s| s.spec.id() == segments[i].spec.id())
295            {
296                bail!("Invalid KeFormat: {value} contains duplicated ids")
297            }
298        }
299        let suffix = &value[segment_start..];
300        if suffix.contains('*') {
301            bail!("Invalid KeFormat: wildcards are only allowed in specs when writing formats")
302        }
303        Ok(KeFormat { storage, suffix })
304    }
305}
306
307impl<'s, Storage: IKeFormatStorage<'s> + 's> core::convert::TryFrom<&KeFormat<'s, Storage>>
308    for String
309{
310    type Error = core::fmt::Error;
311    fn try_from(value: &KeFormat<'s, Storage>) -> Result<Self, Self::Error> {
312        use core::fmt::Write;
313        let mut s = String::new();
314        for segment in value.storage.segments() {
315            s += segment.prefix;
316            write!(&mut s, "{}", segment.spec.pattern())?;
317        }
318        s += value.suffix;
319        Ok(s)
320    }
321}
322impl<'s, Storage: IKeFormatStorage<'s> + 's> core::convert::TryFrom<&KeFormat<'s, Storage>>
323    for OwnedKeyExpr
324{
325    type Error = Error;
326    fn try_from(value: &KeFormat<'s, Storage>) -> Result<Self, Self::Error> {
327        let s: String = value
328            .try_into()
329            .map_err(|e| zerror!("failed to write into String: {e}"))?;
330        OwnedKeyExpr::autocanonize(s)
331    }
332}
333
334impl<'s, Storage: IKeFormatStorage<'s> + 's> core::fmt::Display for KeFormat<'s, Storage> {
335    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
336        for segment in self.storage.segments() {
337            write!(f, "{}{}", segment.prefix, segment.spec)?;
338        }
339        write!(f, "{}", self.suffix)
340    }
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344struct NonMaxU32(NonZeroU32);
345impl NonMaxU32 {
346    fn new(value: u32) -> Option<Self> {
347        NonZeroU32::new(!value).map(NonMaxU32)
348    }
349    fn get(&self) -> u32 {
350        !self.0.get()
351    }
352}
353
354/// An active formatter for a [`KeFormat`].
355#[derive(Clone)]
356pub struct KeFormatter<'s, Storage: IKeFormatStorage<'s>> {
357    format: &'s KeFormat<'s, Storage>,
358    buffer: String,
359    values: Storage::ValuesStorage<Option<(u32, NonMaxU32)>>,
360}
361
362impl<'s, Storage: IKeFormatStorage<'s>> core::fmt::Debug for KeFormatter<'s, Storage> {
363    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364        let values = self.values.as_ref();
365        let segments = self.format.storage.segments();
366        for i in 0..values.len() {
367            let Segment { prefix, spec } = segments[i];
368            let value =
369                values[i].map(|(start, end)| &self.buffer[start as usize..end.get() as usize]);
370            let id = spec.id();
371            let pattern = spec.pattern();
372            let sharp = if id.contains('}')
373                || pattern.contains('}')
374                || value.map_or_else(
375                    || spec.default().is_some_and(|v| v.contains('}')),
376                    |v| v.contains('}'),
377                ) {
378                "#"
379            } else {
380                ""
381            };
382            write!(f, "{prefix}${sharp}{{{id}:{pattern}")?;
383            if let Some(value) = value {
384                write!(f, "={value}")?
385            } else if let Some(default) = spec.default() {
386                write!(f, "#{default}")?
387            };
388            write!(f, "}}{sharp}")?;
389        }
390        write!(f, "{}", self.format.suffix)
391    }
392}
393
394impl<'s, Storage: IKeFormatStorage<'s>> TryFrom<&KeFormatter<'s, Storage>> for OwnedKeyExpr {
395    type Error = Error;
396
397    fn try_from(value: &KeFormatter<'s, Storage>) -> Result<Self, Self::Error> {
398        let values = value.values.as_ref();
399        let segments = value.format.storage.segments();
400        let mut len = value.format.suffix.len();
401        for i in 0..values.len() {
402            len += segments[i].prefix.len()
403                + if let Some((start, end)) = values[i] {
404                    (end.get() - start) as usize
405                } else if let Some(default) = segments[i].spec.default() {
406                    default.len()
407                } else {
408                    bail!("Missing field `{}` in {value:?}", segments[i].spec.id())
409                };
410        }
411        let mut ans = String::with_capacity(len);
412        let mut concatenate = |s: &str| {
413            let skip_slash = matches!(ans.as_bytes().last(), None | Some(b'/'))
414                && s.as_bytes().first() == Some(&b'/');
415            ans += &s[skip_slash as usize..];
416        };
417        for i in 0..values.len() {
418            concatenate(segments[i].prefix);
419            if let Some((start, end)) = values[i] {
420                let end = end.get();
421                concatenate(&value.buffer[start as usize..end as usize])
422            } else if let Some(default) = segments[i].spec.default() {
423                concatenate(default)
424            } else {
425                unsafe { core::hint::unreachable_unchecked() }
426            };
427        }
428        concatenate(value.format.suffix);
429        if ans.ends_with('/') {
430            ans.pop();
431        }
432        OwnedKeyExpr::autocanonize(ans)
433    }
434}
435impl<'s, Storage: IKeFormatStorage<'s>> TryFrom<&mut KeFormatter<'s, Storage>> for OwnedKeyExpr {
436    type Error = Error;
437
438    fn try_from(value: &mut KeFormatter<'s, Storage>) -> Result<Self, Self::Error> {
439        (&*value).try_into()
440    }
441}
442
443#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
444pub enum FormatSetError {
445    InvalidId,
446    PatternNotMatched,
447}
448impl core::fmt::Display for FormatSetError {
449    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
450        write!(f, "{self:?}")
451    }
452}
453impl IError for FormatSetError {}
454impl<'s, Storage: IKeFormatStorage<'s>> KeFormatter<'s, Storage> {
455    /// Access the formatter's format
456    pub fn format(&self) -> &KeFormat<'s, Storage> {
457        self.format
458    }
459
460    /// Clear the formatter of previously set values, without deallocating its internal formatting buffer.
461    pub fn clear(&mut self) -> &mut Self {
462        self.buffer.clear();
463        for value in self.values.as_mut() {
464            *value = None
465        }
466        self
467    }
468
469    /// Build a key-expression according to the format and the currently set values.
470    ///
471    /// This doesn't clear the formatter of already set values, allowing to reuse the builder and only
472    /// change a subset of its properties before building a new key-expression again.
473    pub fn build(&self) -> ZResult<OwnedKeyExpr> {
474        self.try_into()
475    }
476    /// Access the current value for `id`.
477    pub fn get(&self, id: &str) -> Option<&str> {
478        let segments = self.format.storage.segments();
479        segments
480            .iter()
481            .position(|s| s.spec.id() == id)
482            .and_then(|i| {
483                self.values.as_ref()[i]
484                    .map(|(start, end)| &self.buffer[start as usize..end.get() as usize])
485            })
486    }
487    /// Set a new value for `id` using `S`'s [`Display`] formatting.
488    ///
489    /// # Errors
490    /// If the result of `format!("{value}")` is neither:
491    /// - A valid key expression that is included by the pattern for `id`
492    /// - An empty string, on the condition that `id`'s pattern is `**`
493    pub fn set<S: Display>(&mut self, id: &str, value: S) -> Result<&mut Self, FormatSetError> {
494        use core::fmt::Write;
495        let segments = self.format.storage.segments();
496        let Some(i) = segments.iter().position(|s| s.spec.id() == id) else {
497            return Err(FormatSetError::InvalidId);
498        };
499        let values = self.values.as_mut();
500        if let Some((start, end)) = values[i].take() {
501            let end = end.get();
502            let shift = end - start;
503            self.buffer.replace_range(start as usize..end as usize, "");
504            for (s, e) in values.iter_mut().flatten() {
505                if *s < start {
506                    continue;
507                }
508                *s -= shift;
509                *e = NonMaxU32::new(e.get() - shift).unwrap()
510            }
511        }
512        let pattern = segments[i].spec.pattern();
513        let start = self.buffer.len();
514        write!(&mut self.buffer, "{value}").unwrap(); // Writing on `&mut String` should be infallible.
515        let mut set_value = || {
516            let end = self.buffer.len();
517            if start == end {
518                if !pattern.is_double_wild() {
519                    return Err(());
520                }
521            } else {
522                let Ok(ke) = keyexpr::new(&self.buffer[start..end]) else {
523                    return Err(());
524                };
525                if !pattern.includes(ke) {
526                    return Err(());
527                }
528            }
529
530            values[i] = Some((
531                start as u32,
532                NonMaxU32::new(end.try_into().map_err(|_| ())?).ok_or(())?,
533            ));
534            Ok(())
535        };
536        match set_value() {
537            Ok(()) => Ok(self),
538            Err(()) => {
539                self.buffer.truncate(start);
540                Err(FormatSetError::PatternNotMatched)
541            }
542        }
543    }
544}
545
546/// A [`KeFormat`] that owns its format-string.
547pub struct OwnedKeFormat<Storage: IKeFormatStorage<'static> + 'static = Vec<Segment<'static>>> {
548    _owner: Box<str>,
549    format: KeFormat<'static, Storage>,
550}
551impl<Storage: IKeFormatStorage<'static> + 'static> core::ops::Deref for OwnedKeFormat<Storage> {
552    type Target = KeFormat<'static, Storage>;
553    fn deref(&self) -> &Self::Target {
554        &self.format
555    }
556}
557impl<Storage: IKeFormatStorage<'static> + 'static> TryFrom<Box<str>> for OwnedKeFormat<Storage> {
558    type Error = <KeFormat<'static, Storage> as TryFrom<&'static str>>::Error;
559    fn try_from(value: Box<str>) -> Result<Self, Self::Error> {
560        let owner = value;
561        let format: KeFormat<'static, Storage> = unsafe {
562            // This is safe because
563            core::mem::transmute::<&str, &'static str>(&owner)
564        }
565        .try_into()?;
566        Ok(Self {
567            _owner: owner,
568            format,
569        })
570    }
571}
572impl<Storage: IKeFormatStorage<'static> + 'static> TryFrom<String> for OwnedKeFormat<Storage> {
573    type Error = <KeFormat<'static, Storage> as TryFrom<&'static str>>::Error;
574    fn try_from(value: String) -> Result<Self, Self::Error> {
575        value.into_boxed_str().try_into()
576    }
577}
578impl<Storage: IKeFormatStorage<'static> + 'static> core::str::FromStr for OwnedKeFormat<Storage> {
579    type Err = <KeFormat<'static, Storage> as TryFrom<&'static str>>::Error;
580    fn from_str(s: &str) -> Result<Self, Self::Err> {
581        Box::<str>::from(s).try_into()
582    }
583}
584impl<'s, S1: IKeFormatStorage<'s> + 's, S2: IKeFormatStorage<'s> + 's> PartialEq<KeFormat<'s, S2>>
585    for KeFormat<'s, S1>
586{
587    fn eq(&self, other: &KeFormat<'s, S2>) -> bool {
588        self.suffix == other.suffix && self.storage.segments() == other.storage.segments()
589    }
590}
591
592#[test]
593fn formatting() {
594    let format = KeFormat::new("a/${a:*}/b/$#{b:**}#/c").unwrap();
595    assert_eq!(format.storage[0].prefix, "a/");
596    assert_eq!(format.storage[0].spec.id(), "a");
597    assert_eq!(format.storage[0].spec.pattern(), "*");
598    assert_eq!(format.storage[1].prefix, "/b/");
599    assert_eq!(format.storage[1].spec.id(), "b");
600    assert_eq!(format.storage[1].spec.pattern(), "**");
601    assert_eq!(format.suffix, "/c");
602    let ke: OwnedKeyExpr = format
603        .formatter()
604        .set("a", 1)
605        .unwrap()
606        .set("b", "hi/there")
607        .unwrap()
608        .try_into()
609        .unwrap();
610    assert_eq!(ke.as_str(), "a/1/b/hi/there/c");
611    let ke: OwnedKeyExpr = format
612        .formatter()
613        .set("a", 1)
614        .unwrap()
615        .set("b", "")
616        .unwrap()
617        .try_into()
618        .unwrap();
619    assert_eq!(ke.as_str(), "a/1/b/c");
620}
621
622mod parsing;
623pub use parsing::{Iter, Parsed};