Skip to main content

zenoh_protocol/core/
parameters.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/// Module provides a set of utility functions which allows to manipulate  &str` which follows the format `a=b;c=d|e;f=g`.
16/// and structure `Parameters` which provides `HashMap<&str, &str>`-like view over a string of such format.
17///
18/// `;` is the separator between the key-value `(&str, &str)` elements.
19///
20/// `=` is the separator between the `&str`-key and `&str`-value
21///
22/// `|` is the separator between multiple elements of the values.
23use alloc::{
24    borrow::Cow,
25    string::{String, ToString},
26    vec::Vec,
27};
28use core::{borrow::Borrow, fmt};
29#[cfg(feature = "std")]
30use std::collections::HashMap;
31
32pub(super) const LIST_SEPARATOR: char = ';';
33pub(super) const FIELD_SEPARATOR: char = '=';
34pub(super) const VALUE_SEPARATOR: char = '|';
35
36fn split_once(s: &str, c: char) -> (&str, &str) {
37    match s.find(c) {
38        Some(index) => {
39            let (l, r) = s.split_at(index);
40            (l, &r[1..])
41        }
42        None => (s, ""),
43    }
44}
45
46/// Returns an iterator of key-value `(&str, &str)` pairs according to the parameters format.
47pub fn iter(s: &str) -> impl DoubleEndedIterator<Item = (&str, &str)> + Clone {
48    s.split(LIST_SEPARATOR)
49        .filter(|p| !p.is_empty())
50        .map(|p| split_once(p, FIELD_SEPARATOR))
51}
52
53/// Same as [`from_iter_into`] but keys are sorted in alphabetical order.
54pub fn sort<'s, I>(iter: I) -> impl Iterator<Item = (&'s str, &'s str)>
55where
56    I: Iterator<Item = (&'s str, &'s str)>,
57{
58    let mut from = iter.collect::<Vec<(&str, &str)>>();
59    from.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
60    from.into_iter()
61}
62
63/// Joins two key-value `(&str, &str)` iterators removing from `current` any element whose key is present in `new`.
64pub fn join<'s, C, N>(current: C, new: N) -> impl Iterator<Item = (&'s str, &'s str)> + Clone
65where
66    C: Iterator<Item = (&'s str, &'s str)> + Clone,
67    N: Iterator<Item = (&'s str, &'s str)> + Clone + 's,
68{
69    let n = new.clone();
70    let current = current
71        .clone()
72        .filter(move |(kc, _)| !n.clone().any(|(kn, _)| kn == *kc));
73    current.chain(new)
74}
75
76/// Builds a string from an iterator preserving the order.
77#[allow(clippy::should_implement_trait)]
78pub fn from_iter<'s, I>(iter: I) -> String
79where
80    I: Iterator<Item = (&'s str, &'s str)>,
81{
82    let mut into = String::new();
83    from_iter_into(iter, &mut into);
84    into
85}
86
87/// Same as [`from_iter`] but it writes into a user-provided string instead of allocating a new one.
88pub fn from_iter_into<'s, I>(iter: I, into: &mut String)
89where
90    I: Iterator<Item = (&'s str, &'s str)>,
91{
92    concat_into(iter, into);
93}
94
95/// Get the a `&str`-value for a `&str`-key according to the parameters format.
96pub fn get<'s>(s: &'s str, k: &str) -> Option<&'s str> {
97    iter(s).find(|(key, _)| *key == k).map(|(_, value)| value)
98}
99
100/// Get the a `&str`-value iterator for a `&str`-key according to the parameters format.
101pub fn values<'s>(s: &'s str, k: &str) -> impl DoubleEndedIterator<Item = &'s str> {
102    match get(s, k) {
103        Some(v) => v.split(VALUE_SEPARATOR),
104        None => {
105            // Create an empty iterator of the same type as the `Some` case to make the compiler happy.
106            let mut i = "".split(VALUE_SEPARATOR);
107            // Need to skip the first element, as splitting `""` by `"|"` returns `vec![""]`.
108            i.next();
109            i
110        }
111    }
112}
113
114fn _insert<'s, I>(
115    i: I,
116    k: &'s str,
117    v: &'s str,
118) -> (impl Iterator<Item = (&'s str, &'s str)>, Option<&'s str>)
119where
120    I: Iterator<Item = (&'s str, &'s str)> + Clone,
121{
122    let mut iter = i.clone();
123    let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v);
124
125    let current = i.filter(move |x| x.0 != k);
126    let new = Some((k, v)).into_iter();
127    (current.chain(new), item)
128}
129
130/// Insert a key-value `(&str, &str)` pair by appending it at the end of `s` preserving the insertion order.
131pub fn insert<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) {
132    let (iter, item) = _insert(iter(s), k, v);
133    (from_iter(iter), item)
134}
135
136/// Same as [`insert`] but keys are sorted in alphabetical order.
137pub fn insert_sort<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) {
138    let (iter, item) = _insert(iter(s), k, v);
139    (from_iter(sort(iter)), item)
140}
141
142/// Remove a key-value `(&str, &str)` pair from `s` preserving the insertion order.
143pub fn remove<'s>(s: &'s str, k: &str) -> (String, Option<&'s str>) {
144    let mut iter = iter(s);
145    let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v);
146    let iter = iter.filter(|x| x.0 != k);
147    (concat(iter), item)
148}
149
150/// Returns `true` if all keys are sorted in alphabetical order
151pub fn is_ordered(s: &str) -> bool {
152    let mut prev = None;
153    for (k, _) in iter(s) {
154        match prev.take() {
155            Some(p) if k < p => return false,
156            _ => prev = Some(k),
157        }
158    }
159    true
160}
161
162fn concat<'s, I>(iter: I) -> String
163where
164    I: Iterator<Item = (&'s str, &'s str)>,
165{
166    let mut into = String::new();
167    concat_into(iter, &mut into);
168    into
169}
170
171fn concat_into<'s, I>(iter: I, into: &mut String)
172where
173    I: Iterator<Item = (&'s str, &'s str)>,
174{
175    let mut first = true;
176    for (k, v) in iter.filter(|(k, _)| !k.is_empty()) {
177        if !first {
178            into.push(LIST_SEPARATOR);
179        }
180        into.push_str(k);
181        if !v.is_empty() {
182            into.push(FIELD_SEPARATOR);
183            into.push_str(v);
184        }
185        first = false;
186    }
187}
188
189#[cfg(feature = "test")]
190#[doc(hidden)]
191pub fn rand(into: &mut String) {
192    use rand::{
193        distributions::{Alphanumeric, DistString},
194        Rng,
195    };
196
197    const MIN: usize = 2;
198    const MAX: usize = 8;
199
200    let mut rng = rand::thread_rng();
201
202    let num = rng.gen_range(MIN..MAX);
203    for i in 0..num {
204        if i != 0 {
205            into.push(LIST_SEPARATOR);
206        }
207        let len = rng.gen_range(MIN..MAX);
208        let key = Alphanumeric.sample_string(&mut rng, len);
209        into.push_str(key.as_str());
210
211        into.push(FIELD_SEPARATOR);
212
213        let len = rng.gen_range(MIN..MAX);
214        let value = Alphanumeric.sample_string(&mut rng, len);
215        into.push_str(value.as_str());
216    }
217}
218
219/// A map of key/value `(String, Vec<String>)` parameters.
220///
221/// It can be parsed from a `String`, using `;` as separator between each parameter and `=` as separator between a key and its value.
222///
223/// Keys can have multiple values, using `|` as a separator between them. An iterator for these can be obtained with [`Parameters::values`].
224///
225/// Example:
226/// ```
227/// use zenoh_protocol::core::Parameters;
228///
229/// let a = "a=1;b=2;c=3|4|5;d=6";
230/// let p = Parameters::from(a);
231///
232/// // Retrieve values
233/// assert!(!p.is_empty());
234/// assert_eq!(p.get("a").unwrap(), "1");
235/// assert_eq!(p.get("b").unwrap(), "2");
236/// assert_eq!(p.get("c").unwrap(), "3|4|5");
237/// assert_eq!(p.get("d").unwrap(), "6");
238/// assert_eq!(p.values("c").collect::<Vec<&str>>(), vec!["3", "4", "5"]);
239///
240/// // Iterate over parameters
241/// let mut iter = p.iter();
242/// assert_eq!(iter.next().unwrap(), ("a", "1"));
243/// assert_eq!(iter.next().unwrap(), ("b", "2"));
244/// assert_eq!(iter.next().unwrap(), ("c", "3|4|5"));
245/// assert_eq!(iter.next().unwrap(), ("d", "6"));
246/// assert!(iter.next().is_none());
247///
248/// // Create parameters from iterators
249/// let pi = Parameters::from_iter(vec![("a", "1"), ("b", "2"), ("c", "3|4|5"), ("d", "6")]);
250/// assert_eq!(p, pi);
251/// ```
252#[derive(Clone, PartialEq, Eq, Hash, Default)]
253pub struct Parameters<'s>(Cow<'s, str>);
254
255impl<'s> Parameters<'s> {
256    /// Create empty parameters.
257    pub const fn empty() -> Self {
258        Self(Cow::Borrowed(""))
259    }
260
261    /// Returns `true` if parameters does not contain anything.
262    pub fn is_empty(&self) -> bool {
263        self.0.is_empty()
264    }
265
266    /// Returns parameters as [`str`].
267    pub fn as_str(&'s self) -> &'s str {
268        &self.0
269    }
270
271    /// Returns `true` if parameters contains the specified key.
272    pub fn contains_key<K>(&self, k: K) -> bool
273    where
274        K: Borrow<str>,
275    {
276        super::parameters::get(self.as_str(), k.borrow()).is_some()
277    }
278
279    /// Returns a reference to the `&str`-value corresponding to the key.
280    pub fn get<K>(&'s self, k: K) -> Option<&'s str>
281    where
282        K: Borrow<str>,
283    {
284        super::parameters::get(self.as_str(), k.borrow())
285    }
286
287    /// Returns an iterator to the `&str`-values corresponding to the key.
288    pub fn values<K>(&'s self, k: K) -> impl DoubleEndedIterator<Item = &'s str>
289    where
290        K: Borrow<str>,
291    {
292        super::parameters::values(self.as_str(), k.borrow())
293    }
294
295    /// Returns an iterator on the key-value pairs as `(&str, &str)`.
296    pub fn iter(&'s self) -> impl DoubleEndedIterator<Item = (&'s str, &'s str)> + Clone {
297        super::parameters::iter(self.as_str())
298    }
299
300    /// Inserts a key-value pair into the map.
301    /// If the map did not have this key present, [`None`] is returned.
302    /// If the map did have this key present, the value is updated, and the old value is returned.
303    pub fn insert<K, V>(&mut self, k: K, v: V) -> Option<String>
304    where
305        K: Borrow<str>,
306        V: Borrow<str>,
307    {
308        let (inner, item) = super::parameters::insert(self.as_str(), k.borrow(), v.borrow());
309        let item = item.map(|i| i.to_string());
310        self.0 = Cow::Owned(inner);
311        item
312    }
313
314    /// Removes a key from the map, returning the value at the key if the key was previously in the parameters.
315    pub fn remove<K>(&mut self, k: K) -> Option<String>
316    where
317        K: Borrow<str>,
318    {
319        let (inner, item) = super::parameters::remove(self.as_str(), k.borrow());
320        let item = item.map(|i| i.to_string());
321        self.0 = Cow::Owned(inner);
322        item
323    }
324
325    /// Extend these parameters with other parameters.
326    pub fn extend(&mut self, other: &Parameters) {
327        self.extend_from_iter(other.iter());
328    }
329
330    /// Extend these parameters from an iterator.
331    pub fn extend_from_iter<'e, I, K, V>(&mut self, iter: I)
332    where
333        I: Iterator<Item = (&'e K, &'e V)> + Clone,
334        K: Borrow<str> + 'e + ?Sized,
335        V: Borrow<str> + 'e + ?Sized,
336    {
337        let inner = super::parameters::from_iter(super::parameters::join(
338            self.iter(),
339            iter.map(|(k, v)| (k.borrow(), v.borrow())),
340        ));
341        self.0 = Cow::Owned(inner);
342    }
343
344    /// Convert these parameters into owned parameters.
345    pub fn into_owned(self) -> Parameters<'static> {
346        Parameters(Cow::Owned(self.0.into_owned()))
347    }
348
349    /// Returns `true` if all keys are sorted in alphabetical order.
350    pub fn is_ordered(&self) -> bool {
351        super::parameters::is_ordered(self.as_str())
352    }
353}
354
355impl<'s> From<&'s str> for Parameters<'s> {
356    fn from(mut value: &'s str) -> Self {
357        value = value.trim_end_matches(|c| {
358            c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR
359        });
360        Self(Cow::Borrowed(value))
361    }
362}
363
364impl From<String> for Parameters<'_> {
365    fn from(mut value: String) -> Self {
366        let s = value.trim_end_matches(|c| {
367            c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR
368        });
369        value.truncate(s.len());
370        Self(Cow::Owned(value))
371    }
372}
373
374impl<'s> From<Cow<'s, str>> for Parameters<'s> {
375    fn from(value: Cow<'s, str>) -> Self {
376        match value {
377            Cow::Borrowed(s) => Parameters::from(s),
378            Cow::Owned(s) => Parameters::from(s),
379        }
380    }
381}
382
383impl<'a> From<Parameters<'a>> for Cow<'_, Parameters<'a>> {
384    fn from(props: Parameters<'a>) -> Self {
385        Cow::Owned(props)
386    }
387}
388
389impl<'a> From<&'a Parameters<'a>> for Cow<'a, Parameters<'a>> {
390    fn from(props: &'a Parameters<'a>) -> Self {
391        Cow::Borrowed(props)
392    }
393}
394
395impl<'s, K, V> FromIterator<(&'s K, &'s V)> for Parameters<'_>
396where
397    K: Borrow<str> + 's + ?Sized,
398    V: Borrow<str> + 's + ?Sized,
399{
400    fn from_iter<T: IntoIterator<Item = (&'s K, &'s V)>>(iter: T) -> Self {
401        let iter = iter.into_iter();
402        let inner = super::parameters::from_iter(iter.map(|(k, v)| (k.borrow(), v.borrow())));
403        Self(Cow::Owned(inner))
404    }
405}
406
407impl<'s, K, V> FromIterator<&'s (K, V)> for Parameters<'_>
408where
409    K: Borrow<str> + 's,
410    V: Borrow<str> + 's,
411{
412    fn from_iter<T: IntoIterator<Item = &'s (K, V)>>(iter: T) -> Self {
413        Self::from_iter(iter.into_iter().map(|(k, v)| (k.borrow(), v.borrow())))
414    }
415}
416
417impl<'s, K, V> From<&'s [(K, V)]> for Parameters<'_>
418where
419    K: Borrow<str> + 's,
420    V: Borrow<str> + 's,
421{
422    fn from(value: &'s [(K, V)]) -> Self {
423        Self::from_iter(value.iter())
424    }
425}
426
427#[cfg(feature = "std")]
428impl<K, V> From<HashMap<K, V>> for Parameters<'_>
429where
430    K: Borrow<str>,
431    V: Borrow<str>,
432{
433    fn from(map: HashMap<K, V>) -> Self {
434        Self::from_iter(map.iter())
435    }
436}
437
438#[cfg(feature = "std")]
439impl<'s> From<&'s Parameters<'s>> for HashMap<&'s str, &'s str> {
440    fn from(props: &'s Parameters<'s>) -> Self {
441        HashMap::from_iter(props.iter())
442    }
443}
444
445#[cfg(feature = "std")]
446impl From<&Parameters<'_>> for HashMap<String, String> {
447    fn from(props: &Parameters<'_>) -> Self {
448        HashMap::from_iter(props.iter().map(|(k, v)| (k.to_string(), v.to_string())))
449    }
450}
451
452#[cfg(feature = "std")]
453impl<'s> From<&'s Parameters<'s>> for HashMap<Cow<'s, str>, Cow<'s, str>> {
454    fn from(props: &'s Parameters<'s>) -> Self {
455        HashMap::from_iter(props.iter().map(|(k, v)| (Cow::from(k), Cow::from(v))))
456    }
457}
458
459#[cfg(feature = "std")]
460impl From<Parameters<'_>> for HashMap<String, String> {
461    fn from(props: Parameters) -> Self {
462        HashMap::from(&props)
463    }
464}
465
466impl fmt::Display for Parameters<'_> {
467    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468        write!(f, "{}", self.0)
469    }
470}
471
472impl fmt::Debug for Parameters<'_> {
473    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
474        write!(f, "{self}")
475    }
476}
477
478#[cfg(test)]
479mod tests {
480    use super::*;
481
482    #[test]
483    fn test_parameters() {
484        assert!(Parameters::from("").0.is_empty());
485
486        assert_eq!(Parameters::from("p1"), Parameters::from(&[("p1", "")][..]));
487
488        assert_eq!(
489            Parameters::from("p1=v1"),
490            Parameters::from(&[("p1", "v1")][..])
491        );
492
493        assert_eq!(
494            Parameters::from("p1=v1;p2=v2;"),
495            Parameters::from(&[("p1", "v1"), ("p2", "v2")][..])
496        );
497
498        assert_eq!(
499            Parameters::from("p1=v1;p2=v2;|="),
500            Parameters::from(&[("p1", "v1"), ("p2", "v2")][..])
501        );
502
503        assert_eq!(
504            Parameters::from("p1=v1;p2;p3=v3"),
505            Parameters::from(&[("p1", "v1"), ("p2", ""), ("p3", "v3")][..])
506        );
507
508        assert_eq!(
509            Parameters::from("p1=v 1;p 2=v2"),
510            Parameters::from(&[("p1", "v 1"), ("p 2", "v2")][..])
511        );
512
513        assert_eq!(
514            Parameters::from("p1=x=y;p2=a==b"),
515            Parameters::from(&[("p1", "x=y"), ("p2", "a==b")][..])
516        );
517
518        let mut hm: HashMap<String, String> = HashMap::new();
519        hm.insert("p1".to_string(), "v1".to_string());
520        assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
521
522        let mut hm: HashMap<&str, &str> = HashMap::new();
523        hm.insert("p1", "v1");
524        assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
525
526        let mut hm: HashMap<Cow<str>, Cow<str>> = HashMap::new();
527        hm.insert(Cow::from("p1"), Cow::from("v1"));
528        assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
529    }
530
531    #[test]
532    fn values_iterator_for_non_existing_key_is_empty() {
533        let params = Parameters::from("p1=1");
534
535        assert_eq!(params.values("p2").next(), None);
536    }
537}