diff --git a/commons/zenoh-protocol/src/core/endpoint.rs b/commons/zenoh-protocol/src/core/endpoint.rs index 1c9ccffb40..3c6f3dad1b 100644 --- a/commons/zenoh-protocol/src/core/endpoint.rs +++ b/commons/zenoh-protocol/src/core/endpoint.rs @@ -16,7 +16,7 @@ use core::{borrow::Borrow, convert::TryFrom, fmt, str::FromStr}; use zenoh_result::{bail, zerror, Error as ZError, ZResult}; -use super::{locator::*, parameters::Parameters}; +use super::{locator::*, parameters_view::ParametersView}; // Parsing chars pub const PROTO_SEPARATOR: char = '/'; @@ -196,15 +196,15 @@ impl<'a> Metadata<'a> { } pub fn iter(&'a self) -> impl DoubleEndedIterator + Clone { - Parameters::iter(self.0) + ParametersView::iter(self.0) } pub fn get(&'a self, k: &str) -> Option<&'a str> { - Parameters::get(self.0, k) + ParametersView::get(self.0, k) } pub fn values(&'a self, k: &str) -> impl DoubleEndedIterator { - Parameters::values(self.0, k) + ParametersView::values(self.0, k) } } @@ -250,7 +250,7 @@ impl MetadataMut<'_> { let ep = EndPoint::new( self.0.protocol(), self.0.address(), - Parameters::from_iter(Parameters::sort(Parameters::join( + ParametersView::from_iter(ParametersView::sort(ParametersView::join( self.0.metadata().iter(), iter.map(|(k, v)| (k.borrow(), v.borrow())), ))), @@ -269,7 +269,7 @@ impl MetadataMut<'_> { let ep = EndPoint::new( self.0.protocol(), self.0.address(), - Parameters::insert_sort(self.0.metadata().as_str(), k.borrow(), v.borrow()).0, + ParametersView::insert_sort(self.0.metadata().as_str(), k.borrow(), v.borrow()).0, self.0.config(), )?; @@ -284,7 +284,7 @@ impl MetadataMut<'_> { let ep = EndPoint::new( self.0.protocol(), self.0.address(), - Parameters::remove(self.0.metadata().as_str(), k.borrow()).0, + ParametersView::remove(self.0.metadata().as_str(), k.borrow()).0, self.0.config(), )?; @@ -326,15 +326,15 @@ impl<'a> Config<'a> { } pub fn iter(&'a self) -> impl DoubleEndedIterator + Clone { - Parameters::iter(self.0) + ParametersView::iter(self.0) } pub fn get(&'a self, k: &str) -> Option<&'a str> { - Parameters::get(self.0, k) + ParametersView::get(self.0, k) } pub fn values(&'a self, k: &str) -> impl DoubleEndedIterator { - Parameters::values(self.0, k) + ParametersView::values(self.0, k) } } @@ -381,7 +381,7 @@ impl ConfigMut<'_> { self.0.protocol(), self.0.address(), self.0.metadata(), - Parameters::from_iter(Parameters::sort(Parameters::join( + ParametersView::from_iter(ParametersView::sort(ParametersView::join( self.0.config().iter(), iter.map(|(k, v)| (k.borrow(), v.borrow())), ))), @@ -400,7 +400,7 @@ impl ConfigMut<'_> { self.0.protocol(), self.0.address(), self.0.metadata(), - Parameters::insert_sort(self.0.config().as_str(), k.borrow(), v.borrow()).0, + ParametersView::insert_sort(self.0.config().as_str(), k.borrow(), v.borrow()).0, )?; self.0.inner = ep.inner; @@ -415,7 +415,7 @@ impl ConfigMut<'_> { self.0.protocol(), self.0.address(), self.0.metadata(), - Parameters::remove(self.0.config().as_str(), k.borrow()).0, + ParametersView::remove(self.0.config().as_str(), k.borrow()).0, )?; self.0.inner = ep.inner; @@ -577,8 +577,8 @@ impl TryFrom for EndPoint { (Some(midx), None) if midx > pidx && !s[midx + 1..].is_empty() => { let mut inner = String::with_capacity(s.len()); inner.push_str(&s[..midx + 1]); // Includes metadata separator - Parameters::from_iter_into( - Parameters::sort(Parameters::iter(&s[midx + 1..])), + ParametersView::from_iter_into( + ParametersView::sort(ParametersView::iter(&s[midx + 1..])), &mut inner, ); Ok(EndPoint { inner }) @@ -587,8 +587,8 @@ impl TryFrom for EndPoint { (None, Some(cidx)) if cidx > pidx && !s[cidx + 1..].is_empty() => { let mut inner = String::with_capacity(s.len()); inner.push_str(&s[..cidx + 1]); // Includes config separator - Parameters::from_iter_into( - Parameters::sort(Parameters::iter(&s[cidx + 1..])), + ParametersView::from_iter_into( + ParametersView::sort(ParametersView::iter(&s[cidx + 1..])), &mut inner, ); Ok(EndPoint { inner }) @@ -603,14 +603,14 @@ impl TryFrom for EndPoint { let mut inner = String::with_capacity(s.len()); inner.push_str(&s[..midx + 1]); // Includes metadata separator - Parameters::from_iter_into( - Parameters::sort(Parameters::iter(&s[midx + 1..cidx])), + ParametersView::from_iter_into( + ParametersView::sort(ParametersView::iter(&s[midx + 1..cidx])), &mut inner, ); inner.push(CONFIG_SEPARATOR); - Parameters::from_iter_into( - Parameters::sort(Parameters::iter(&s[cidx + 1..])), + ParametersView::from_iter_into( + ParametersView::sort(ParametersView::iter(&s[cidx + 1..])), &mut inner, ); @@ -655,11 +655,11 @@ impl EndPoint { if rng.gen_bool(0.5) { endpoint.push(METADATA_SEPARATOR); - Parameters::rand(&mut endpoint); + ParametersView::rand(&mut endpoint); } if rng.gen_bool(0.5) { endpoint.push(CONFIG_SEPARATOR); - Parameters::rand(&mut endpoint); + ParametersView::rand(&mut endpoint); } endpoint.parse().unwrap() diff --git a/commons/zenoh-protocol/src/core/mod.rs b/commons/zenoh-protocol/src/core/mod.rs index 9c8eee58a1..47f240e8ef 100644 --- a/commons/zenoh-protocol/src/core/mod.rs +++ b/commons/zenoh-protocol/src/core/mod.rs @@ -53,12 +53,12 @@ pub use endpoint::*; pub mod resolution; pub use resolution::*; +pub mod parameters_view; +pub use parameters_view::*; + pub mod parameters; pub use parameters::*; -pub mod properties; -pub use properties::*; - /// The global unique id of a zenoh peer. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] diff --git a/commons/zenoh-protocol/src/core/parameters.rs b/commons/zenoh-protocol/src/core/parameters.rs index e4f815feff..b5cfc92e05 100644 --- a/commons/zenoh-protocol/src/core/parameters.rs +++ b/commons/zenoh-protocol/src/core/parameters.rs @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 ZettaScale Technology +// Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at @@ -11,201 +11,519 @@ // Contributors: // ZettaScale Zenoh Team, // -pub(super) const LIST_SEPARATOR: char = ';'; -pub(super) const FIELD_SEPARATOR: char = '='; -pub(super) const VALUE_SEPARATOR: char = '|'; +use alloc::{ + borrow::Cow, + string::{String, ToString}, +}; +use core::{borrow::Borrow, fmt}; +#[cfg(feature = "std")] +use std::collections::HashMap; -use alloc::{string::String, vec::Vec}; +use super::parameters_view::{ParametersView, FIELD_SEPARATOR, LIST_SEPARATOR, VALUE_SEPARATOR}; -fn split_once(s: &str, c: char) -> (&str, &str) { - match s.find(c) { - Some(index) => { - let (l, r) = s.split_at(index); - (l, &r[1..]) - } - None => (s, ""), - } -} - -/// Parameters provides an `HashMap<&str, &str>`-like view over a `&str` when `&str` follows the format `a=b;c=d|e;f=g`. +/// A map of key/value (String,String) properties. +/// It can be parsed from a String, using `;` or `` as separator between each properties +/// and `=` as separator between a key and its value. Keys and values are trimed. +/// +/// Example: +/// ``` +/// use zenoh_protocol::core::Properties; /// -/// `;` is the separator between the key-value `(&str, &str)` elements. +/// let a = "a=1;b=2;c=3|4|5;d=6"; +/// let p = Properties::from(a); /// -/// `=` is the separator between the `&str`-key and `&str`-value +/// // Retrieve values +/// assert!(!p.is_empty()); +/// assert_eq!(p.get("a").unwrap(), "1"); +/// assert_eq!(p.get("b").unwrap(), "2"); +/// assert_eq!(p.get("c").unwrap(), "3|4|5"); +/// assert_eq!(p.get("d").unwrap(), "6"); +/// assert_eq!(p.values("c").collect::>(), vec!["3", "4", "5"]); /// -/// `|` is the separator between multiple elements of the values. -pub struct Parameters; +/// // Iterate over properties +/// let mut iter = p.iter(); +/// assert_eq!(iter.next().unwrap(), ("a", "1")); +/// assert_eq!(iter.next().unwrap(), ("b", "2")); +/// assert_eq!(iter.next().unwrap(), ("c", "3|4|5")); +/// assert_eq!(iter.next().unwrap(), ("d", "6")); +/// assert!(iter.next().is_none()); +/// +/// // Create properties from iterators +/// let pi = Properties::from_iter(vec![("a", "1"), ("b", "2"), ("c", "3|4|5"), ("d", "6")]); +/// assert_eq!(p, pi); +/// ``` +#[derive(Clone, PartialEq, Eq, Hash, Default)] +pub struct Parameters<'s>(Cow<'s, str>); + +impl<'s> Parameters<'s> { + /// Returns `true` if properties does not contain anything. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } -impl Parameters { - /// Returns an iterator of key-value `(&str, &str)` pairs according to the parameters format. - pub fn iter(s: &str) -> impl DoubleEndedIterator + Clone { - s.split(LIST_SEPARATOR) - .filter(|p| !p.is_empty()) - .map(|p| split_once(p, FIELD_SEPARATOR)) + /// Returns properties as [`str`]. + pub fn as_str(&'s self) -> &'s str { + &self.0 } - /// Same as [`Self::from_iter_into`] but keys are sorted in alphabetical order. - pub fn sort<'s, I>(iter: I) -> impl Iterator + /// Returns `true` if properties contains the specified key. + pub fn contains_key(&self, k: K) -> bool where - I: Iterator, + K: Borrow, { - let mut from = iter.collect::>(); - from.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2)); - from.into_iter() + ParametersView::get(self.as_str(), k.borrow()).is_some() } - /// Joins two key-value `(&str, &str)` iterators removing from `current` any element whose key is present in `new`. - pub fn join<'s, C, N>(current: C, new: N) -> impl Iterator + Clone + /// Returns a reference to the `&str`-value corresponding to the key. + pub fn get(&'s self, k: K) -> Option<&'s str> where - C: Iterator + Clone, - N: Iterator + Clone + 's, + K: Borrow, { - let n = new.clone(); - let current = current - .clone() - .filter(move |(kc, _)| !n.clone().any(|(kn, _)| kn == *kc)); - current.chain(new) + ParametersView::get(self.as_str(), k.borrow()) } - /// Builds a string from an iterator preserving the order. - #[allow(clippy::should_implement_trait)] - pub fn from_iter<'s, I>(iter: I) -> String + /// Returns an iterator to the `&str`-values corresponding to the key. + pub fn values(&'s self, k: K) -> impl DoubleEndedIterator where - I: Iterator, + K: Borrow, { - let mut into = String::new(); - Parameters::from_iter_into(iter, &mut into); - into + ParametersView::values(self.as_str(), k.borrow()) } - /// Same as [`Self::from_iter`] but it writes into a user-provided string instead of allocating a new one. - pub fn from_iter_into<'s, I>(iter: I, into: &mut String) + /// Returns an iterator on the key-value pairs as `(&str, &str)`. + pub fn iter(&'s self) -> impl DoubleEndedIterator + Clone { + ParametersView::iter(self.as_str()) + } + + /// Inserts a key-value pair into the map. + /// If the map did not have this key present, [`None`]` is returned. + /// If the map did have this key present, the value is updated, and the old value is returned. + pub fn insert(&mut self, k: K, v: V) -> Option where - I: Iterator, + K: Borrow, + V: Borrow, { - Parameters::concat_into(iter, into); + let (inner, item) = ParametersView::insert(self.as_str(), k.borrow(), v.borrow()); + let item = item.map(|i| i.to_string()); + self.0 = Cow::Owned(inner); + item } - /// Get the a `&str`-value for a `&str`-key according to the parameters format. - pub fn get<'s>(s: &'s str, k: &str) -> Option<&'s str> { - Parameters::iter(s) - .find(|(key, _)| *key == k) - .map(|(_, value)| value) + /// Removes a key from the map, returning the value at the key if the key was previously in the properties. + pub fn remove(&mut self, k: K) -> Option + where + K: Borrow, + { + let (inner, item) = ParametersView::remove(self.as_str(), k.borrow()); + let item = item.map(|i| i.to_string()); + self.0 = Cow::Owned(inner); + item } - /// Get the a `&str`-value iterator for a `&str`-key according to the parameters format. - pub fn values<'s>(s: &'s str, k: &str) -> impl DoubleEndedIterator { - match Parameters::get(s, k) { - Some(v) => v.split(VALUE_SEPARATOR), - None => { - let mut i = "".split(VALUE_SEPARATOR); - i.next(); - i - } - } + /// Extend these properties with other properties. + pub fn extend(&mut self, other: &Parameters) { + self.extend_from_iter(other.iter()); } - fn _insert<'s, I>( - i: I, - k: &'s str, - v: &'s str, - ) -> (impl Iterator, Option<&'s str>) + /// Extend these properties from an iterator. + pub fn extend_from_iter<'e, I, K, V>(&mut self, iter: I) where - I: Iterator + Clone, + I: Iterator + Clone, + K: Borrow + 'e + ?Sized, + V: Borrow + 'e + ?Sized, { - let mut iter = i.clone(); - let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v); + let inner = ParametersView::from_iter(ParametersView::join( + self.iter(), + iter.map(|(k, v)| (k.borrow(), v.borrow())), + )); + self.0 = Cow::Owned(inner); + } - let current = i.filter(move |x| x.0 != k); - let new = Some((k, v)).into_iter(); - (current.chain(new), item) + /// Convert these properties into owned properties. + pub fn into_owned(self) -> Parameters<'static> { + Parameters(Cow::Owned(self.0.into_owned())) } - /// Insert a key-value `(&str, &str)` pair by appending it at the end of `s` preserving the insertion order. - pub fn insert<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) { - let (iter, item) = Parameters::_insert(Parameters::iter(s), k, v); - (Parameters::from_iter(iter), item) + /// Returns `true`` if all keys are sorted in alphabetical order. + pub fn is_ordered(&self) -> bool { + ParametersView::is_ordered(self.as_str()) } +} - /// Same as [`Self::insert`] but keys are sorted in alphabetical order. - pub fn insert_sort<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) { - let (iter, item) = Parameters::_insert(Parameters::iter(s), k, v); - (Parameters::from_iter(Parameters::sort(iter)), item) +impl<'s> From<&'s str> for Parameters<'s> { + fn from(mut value: &'s str) -> Self { + value = value.trim_end_matches(|c| { + c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR + }); + Self(Cow::Borrowed(value)) } +} - /// Remove a key-value `(&str, &str)` pair from `s` preserving the insertion order. - pub fn remove<'s>(s: &'s str, k: &str) -> (String, Option<&'s str>) { - let mut iter = Parameters::iter(s); - let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v); - let iter = iter.filter(|x| x.0 != k); - (Parameters::concat(iter), item) +impl From for Parameters<'_> { + fn from(mut value: String) -> Self { + let s = value.trim_end_matches(|c| { + c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR + }); + value.truncate(s.len()); + Self(Cow::Owned(value)) } +} - /// Returns `true` if all keys are sorted in alphabetical order - pub fn is_ordered(s: &str) -> bool { - let mut prev = None; - for (k, _) in Parameters::iter(s) { - match prev.take() { - Some(p) if k < p => return false, - _ => prev = Some(k), - } +impl<'s> From> for Parameters<'s> { + fn from(value: Cow<'s, str>) -> Self { + match value { + Cow::Borrowed(s) => Parameters::from(s), + Cow::Owned(s) => Parameters::from(s), } - true } +} - fn concat<'s, I>(iter: I) -> String +impl<'a> From> for Cow<'_, Parameters<'a>> { + fn from(props: Parameters<'a>) -> Self { + Cow::Owned(props) + } +} + +impl<'a> From<&'a Parameters<'a>> for Cow<'a, Parameters<'a>> { + fn from(props: &'a Parameters<'a>) -> Self { + Cow::Borrowed(props) + } +} + +impl<'s, K, V> FromIterator<(&'s K, &'s V)> for Parameters<'_> +where + K: Borrow + 's + ?Sized, + V: Borrow + 's + ?Sized, +{ + fn from_iter>(iter: T) -> Self { + let iter = iter.into_iter(); + let inner = ParametersView::from_iter(iter.map(|(k, v)| (k.borrow(), v.borrow()))); + Self(Cow::Owned(inner)) + } +} + +impl<'s, K, V> FromIterator<&'s (K, V)> for Parameters<'_> +where + K: Borrow + 's, + V: Borrow + 's, +{ + fn from_iter>(iter: T) -> Self { + Self::from_iter(iter.into_iter().map(|(k, v)| (k.borrow(), v.borrow()))) + } +} + +impl<'s, K, V> From<&'s [(K, V)]> for Parameters<'_> +where + K: Borrow + 's, + V: Borrow + 's, +{ + fn from(value: &'s [(K, V)]) -> Self { + Self::from_iter(value.iter()) + } +} + +#[cfg(feature = "std")] +impl From> for Parameters<'_> +where + K: Borrow, + V: Borrow, +{ + fn from(map: HashMap) -> Self { + Self::from_iter(map.iter()) + } +} + +#[cfg(feature = "std")] +impl<'s> From<&'s Parameters<'s>> for HashMap<&'s str, &'s str> { + fn from(props: &'s Parameters<'s>) -> Self { + HashMap::from_iter(props.iter()) + } +} + +#[cfg(feature = "std")] +impl From<&Parameters<'_>> for HashMap { + fn from(props: &Parameters<'_>) -> Self { + HashMap::from_iter(props.iter().map(|(k, v)| (k.to_string(), v.to_string()))) + } +} + +#[cfg(feature = "std")] +impl<'s> From<&'s Parameters<'s>> for HashMap, Cow<'s, str>> { + fn from(props: &'s Parameters<'s>) -> Self { + HashMap::from_iter(props.iter().map(|(k, v)| (Cow::from(k), Cow::from(v)))) + } +} + +#[cfg(feature = "std")] +impl From> for HashMap { + fn from(props: Parameters) -> Self { + HashMap::from(&props) + } +} + +impl fmt::Display for Parameters<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl fmt::Debug for Parameters<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Default)] +pub struct OrderedProperties<'s>(Parameters<'s>); + +impl<'s> OrderedProperties<'s> { + /// Returns `true` if properties does not contain anything. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns properties as [`str`]. + pub fn as_str(&'s self) -> &'s str { + self.0.as_str() + } + + /// Returns `true` if properties contains the specified key. + pub fn contains_key(&self, k: K) -> bool where - I: Iterator, + K: Borrow, { - let mut into = String::new(); - Parameters::concat_into(iter, &mut into); - into + self.0.contains_key(k) } - fn concat_into<'s, I>(iter: I, into: &mut String) + /// Returns a reference to the `&str`-value corresponding to the key. + pub fn get(&'s self, k: K) -> Option<&'s str> where - I: Iterator, + K: Borrow, { - let mut first = true; - for (k, v) in iter.filter(|(k, _)| !k.is_empty()) { - if !first { - into.push(LIST_SEPARATOR); - } - into.push_str(k); - if !v.is_empty() { - into.push(FIELD_SEPARATOR); - into.push_str(v); - } - first = false; - } + self.0.get(k) } - #[cfg(feature = "test")] - pub fn rand(into: &mut String) { - use rand::{ - distributions::{Alphanumeric, DistString}, - Rng, - }; + /// Returns an iterator to the `&str`-values corresponding to the key. + pub fn values(&'s self, k: K) -> impl DoubleEndedIterator + where + K: Borrow, + { + self.0.values(k) + } - const MIN: usize = 2; - const MAX: usize = 8; + /// Returns an iterator on the key-value pairs as `(&str, &str)`. + pub fn iter(&'s self) -> impl DoubleEndedIterator + Clone { + self.0.iter() + } - let mut rng = rand::thread_rng(); + /// Removes a key from the map, returning the value at the key if the key was previously in the properties. + pub fn remove(&mut self, k: K) -> Option + where + K: Borrow, + { + self.0.remove(k) + } - let num = rng.gen_range(MIN..MAX); - for i in 0..num { - if i != 0 { - into.push(LIST_SEPARATOR); - } - let len = rng.gen_range(MIN..MAX); - let key = Alphanumeric.sample_string(&mut rng, len); - into.push_str(key.as_str()); + /// Inserts a key-value pair into the map. + /// If the map did not have this key present, [`None`]` is returned. + /// If the map did have this key present, the value is updated, and the old value is returned. + pub fn insert(&mut self, k: K, v: V) -> Option + where + K: Borrow, + V: Borrow, + { + let item = self.0.insert(k, v); + self.order(); + item + } - into.push(FIELD_SEPARATOR); + /// Extend these properties with other properties. + pub fn extend(&mut self, other: &Parameters) { + self.extend_from_iter(other.iter()); + } - let len = rng.gen_range(MIN..MAX); - let value = Alphanumeric.sample_string(&mut rng, len); - into.push_str(value.as_str()); + /// Extend these properties from an iterator. + pub fn extend_from_iter<'e, I, K, V>(&mut self, iter: I) + where + I: Iterator + Clone, + K: Borrow + 'e + ?Sized, + V: Borrow + 'e + ?Sized, + { + self.0.extend_from_iter(iter); + self.order(); + } + + /// Convert these properties into owned properties. + pub fn into_owned(self) -> OrderedProperties<'static> { + OrderedProperties(self.0.into_owned()) + } + + fn order(&mut self) { + if !self.0.is_ordered() { + self.0 = Parameters(Cow::Owned(ParametersView::from_iter(ParametersView::sort( + self.iter(), + )))); } } } + +impl<'s> From> for OrderedProperties<'s> { + fn from(value: Parameters<'s>) -> Self { + let mut props = Self(value); + props.order(); + props + } +} + +impl<'s> From<&'s str> for OrderedProperties<'s> { + fn from(value: &'s str) -> Self { + Self::from(Parameters::from(value)) + } +} + +impl From for OrderedProperties<'_> { + fn from(value: String) -> Self { + Self::from(Parameters::from(value)) + } +} + +impl<'s> From> for OrderedProperties<'s> { + fn from(value: Cow<'s, str>) -> Self { + Self::from(Parameters::from(value)) + } +} + +impl<'s, K, V> FromIterator<(&'s K, &'s V)> for OrderedProperties<'_> +where + K: Borrow + 's + ?Sized, + V: Borrow + 's + ?Sized, +{ + fn from_iter>(iter: T) -> Self { + Self::from(Parameters::from_iter(iter)) + } +} + +impl<'s, K, V> FromIterator<&'s (K, V)> for OrderedProperties<'_> +where + K: Borrow + 's, + V: Borrow + 's, +{ + fn from_iter>(iter: T) -> Self { + Self::from(Parameters::from_iter(iter)) + } +} + +impl<'s, K, V> From<&'s [(K, V)]> for OrderedProperties<'_> +where + K: Borrow + 's, + V: Borrow + 's, +{ + fn from(value: &'s [(K, V)]) -> Self { + Self::from_iter(value.iter()) + } +} + +#[cfg(feature = "std")] +impl From> for OrderedProperties<'_> +where + K: Borrow, + V: Borrow, +{ + fn from(map: HashMap) -> Self { + Self::from_iter(map.iter()) + } +} + +#[cfg(feature = "std")] +impl<'s> From<&'s OrderedProperties<'s>> for HashMap<&'s str, &'s str> { + fn from(props: &'s OrderedProperties<'s>) -> Self { + HashMap::from(&props.0) + } +} + +#[cfg(feature = "std")] +impl From<&OrderedProperties<'_>> for HashMap { + fn from(props: &OrderedProperties<'_>) -> Self { + HashMap::from(&props.0) + } +} + +#[cfg(feature = "std")] +impl<'s> From<&'s OrderedProperties<'s>> for HashMap, Cow<'s, str>> { + fn from(props: &'s OrderedProperties<'s>) -> Self { + HashMap::from(&props.0) + } +} + +#[cfg(feature = "std")] +impl From> for HashMap { + fn from(props: OrderedProperties) -> Self { + HashMap::from(&props) + } +} + +impl fmt::Display for OrderedProperties<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl fmt::Debug for OrderedProperties<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_properties() { + assert!(Parameters::from("").0.is_empty()); + + assert_eq!(Parameters::from("p1"), Parameters::from(&[("p1", "")][..])); + + assert_eq!( + Parameters::from("p1=v1"), + Parameters::from(&[("p1", "v1")][..]) + ); + + assert_eq!( + Parameters::from("p1=v1;p2=v2;"), + Parameters::from(&[("p1", "v1"), ("p2", "v2")][..]) + ); + + assert_eq!( + Parameters::from("p1=v1;p2=v2;|="), + Parameters::from(&[("p1", "v1"), ("p2", "v2")][..]) + ); + + assert_eq!( + Parameters::from("p1=v1;p2;p3=v3"), + Parameters::from(&[("p1", "v1"), ("p2", ""), ("p3", "v3")][..]) + ); + + assert_eq!( + Parameters::from("p1=v 1;p 2=v2"), + Parameters::from(&[("p1", "v 1"), ("p 2", "v2")][..]) + ); + + assert_eq!( + Parameters::from("p1=x=y;p2=a==b"), + Parameters::from(&[("p1", "x=y"), ("p2", "a==b")][..]) + ); + + let mut hm: HashMap = HashMap::new(); + hm.insert("p1".to_string(), "v1".to_string()); + assert_eq!(Parameters::from(hm), Parameters::from("p1=v1")); + + let mut hm: HashMap<&str, &str> = HashMap::new(); + hm.insert("p1", "v1"); + assert_eq!(Parameters::from(hm), Parameters::from("p1=v1")); + + let mut hm: HashMap, Cow> = HashMap::new(); + hm.insert(Cow::from("p1"), Cow::from("v1")); + assert_eq!(Parameters::from(hm), Parameters::from("p1=v1")); + } +} diff --git a/commons/zenoh-protocol/src/core/parameters_view.rs b/commons/zenoh-protocol/src/core/parameters_view.rs new file mode 100644 index 0000000000..adcf0ea0fb --- /dev/null +++ b/commons/zenoh-protocol/src/core/parameters_view.rs @@ -0,0 +1,211 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +pub(super) const LIST_SEPARATOR: char = ';'; +pub(super) const FIELD_SEPARATOR: char = '='; +pub(super) const VALUE_SEPARATOR: char = '|'; + +use alloc::{string::String, vec::Vec}; + +fn split_once(s: &str, c: char) -> (&str, &str) { + match s.find(c) { + Some(index) => { + let (l, r) = s.split_at(index); + (l, &r[1..]) + } + None => (s, ""), + } +} + +/// Parameters provides an `HashMap<&str, &str>`-like view over a `&str` when `&str` follows the format `a=b;c=d|e;f=g`. +/// +/// `;` is the separator between the key-value `(&str, &str)` elements. +/// +/// `=` is the separator between the `&str`-key and `&str`-value +/// +/// `|` is the separator between multiple elements of the values. +pub struct ParametersView; + +impl ParametersView { + /// Returns an iterator of key-value `(&str, &str)` pairs according to the parameters format. + pub fn iter(s: &str) -> impl DoubleEndedIterator + Clone { + s.split(LIST_SEPARATOR) + .filter(|p| !p.is_empty()) + .map(|p| split_once(p, FIELD_SEPARATOR)) + } + + /// Same as [`Self::from_iter_into`] but keys are sorted in alphabetical order. + pub fn sort<'s, I>(iter: I) -> impl Iterator + where + I: Iterator, + { + let mut from = iter.collect::>(); + from.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2)); + from.into_iter() + } + + /// Joins two key-value `(&str, &str)` iterators removing from `current` any element whose key is present in `new`. + pub fn join<'s, C, N>(current: C, new: N) -> impl Iterator + Clone + where + C: Iterator + Clone, + N: Iterator + Clone + 's, + { + let n = new.clone(); + let current = current + .clone() + .filter(move |(kc, _)| !n.clone().any(|(kn, _)| kn == *kc)); + current.chain(new) + } + + /// Builds a string from an iterator preserving the order. + #[allow(clippy::should_implement_trait)] + pub fn from_iter<'s, I>(iter: I) -> String + where + I: Iterator, + { + let mut into = String::new(); + ParametersView::from_iter_into(iter, &mut into); + into + } + + /// Same as [`Self::from_iter`] but it writes into a user-provided string instead of allocating a new one. + pub fn from_iter_into<'s, I>(iter: I, into: &mut String) + where + I: Iterator, + { + ParametersView::concat_into(iter, into); + } + + /// Get the a `&str`-value for a `&str`-key according to the parameters format. + pub fn get<'s>(s: &'s str, k: &str) -> Option<&'s str> { + ParametersView::iter(s) + .find(|(key, _)| *key == k) + .map(|(_, value)| value) + } + + /// Get the a `&str`-value iterator for a `&str`-key according to the parameters format. + pub fn values<'s>(s: &'s str, k: &str) -> impl DoubleEndedIterator { + match ParametersView::get(s, k) { + Some(v) => v.split(VALUE_SEPARATOR), + None => { + let mut i = "".split(VALUE_SEPARATOR); + i.next(); + i + } + } + } + + fn _insert<'s, I>( + i: I, + k: &'s str, + v: &'s str, + ) -> (impl Iterator, Option<&'s str>) + where + I: Iterator + Clone, + { + let mut iter = i.clone(); + let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v); + + let current = i.filter(move |x| x.0 != k); + let new = Some((k, v)).into_iter(); + (current.chain(new), item) + } + + /// Insert a key-value `(&str, &str)` pair by appending it at the end of `s` preserving the insertion order. + pub fn insert<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) { + let (iter, item) = ParametersView::_insert(ParametersView::iter(s), k, v); + (ParametersView::from_iter(iter), item) + } + + /// Same as [`Self::insert`] but keys are sorted in alphabetical order. + pub fn insert_sort<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) { + let (iter, item) = ParametersView::_insert(ParametersView::iter(s), k, v); + (ParametersView::from_iter(ParametersView::sort(iter)), item) + } + + /// Remove a key-value `(&str, &str)` pair from `s` preserving the insertion order. + pub fn remove<'s>(s: &'s str, k: &str) -> (String, Option<&'s str>) { + let mut iter = ParametersView::iter(s); + let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v); + let iter = iter.filter(|x| x.0 != k); + (ParametersView::concat(iter), item) + } + + /// Returns `true` if all keys are sorted in alphabetical order + pub fn is_ordered(s: &str) -> bool { + let mut prev = None; + for (k, _) in ParametersView::iter(s) { + match prev.take() { + Some(p) if k < p => return false, + _ => prev = Some(k), + } + } + true + } + + fn concat<'s, I>(iter: I) -> String + where + I: Iterator, + { + let mut into = String::new(); + ParametersView::concat_into(iter, &mut into); + into + } + + fn concat_into<'s, I>(iter: I, into: &mut String) + where + I: Iterator, + { + let mut first = true; + for (k, v) in iter.filter(|(k, _)| !k.is_empty()) { + if !first { + into.push(LIST_SEPARATOR); + } + into.push_str(k); + if !v.is_empty() { + into.push(FIELD_SEPARATOR); + into.push_str(v); + } + first = false; + } + } + + #[cfg(feature = "test")] + pub fn rand(into: &mut String) { + use rand::{ + distributions::{Alphanumeric, DistString}, + Rng, + }; + + const MIN: usize = 2; + const MAX: usize = 8; + + let mut rng = rand::thread_rng(); + + let num = rng.gen_range(MIN..MAX); + for i in 0..num { + if i != 0 { + into.push(LIST_SEPARATOR); + } + let len = rng.gen_range(MIN..MAX); + let key = Alphanumeric.sample_string(&mut rng, len); + into.push_str(key.as_str()); + + into.push(FIELD_SEPARATOR); + + let len = rng.gen_range(MIN..MAX); + let value = Alphanumeric.sample_string(&mut rng, len); + into.push_str(value.as_str()); + } + } +} diff --git a/commons/zenoh-protocol/src/core/properties.rs b/commons/zenoh-protocol/src/core/properties.rs deleted file mode 100644 index 5264288448..0000000000 --- a/commons/zenoh-protocol/src/core/properties.rs +++ /dev/null @@ -1,517 +0,0 @@ -// -// Copyright (c) 2022 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use alloc::{ - borrow::Cow, - string::{String, ToString}, -}; -use core::{borrow::Borrow, fmt}; -#[cfg(feature = "std")] -use std::collections::HashMap; - -use super::parameters::{Parameters, FIELD_SEPARATOR, LIST_SEPARATOR, VALUE_SEPARATOR}; - -/// A map of key/value (String,String) properties. -/// It can be parsed from a String, using `;` or `` as separator between each properties -/// and `=` as separator between a key and its value. Keys and values are trimed. -/// -/// Example: -/// ``` -/// use zenoh_protocol::core::Properties; -/// -/// let a = "a=1;b=2;c=3|4|5;d=6"; -/// let p = Properties::from(a); -/// -/// // Retrieve values -/// assert!(!p.is_empty()); -/// assert_eq!(p.get("a").unwrap(), "1"); -/// assert_eq!(p.get("b").unwrap(), "2"); -/// assert_eq!(p.get("c").unwrap(), "3|4|5"); -/// assert_eq!(p.get("d").unwrap(), "6"); -/// assert_eq!(p.values("c").collect::>(), vec!["3", "4", "5"]); -/// -/// // Iterate over properties -/// let mut iter = p.iter(); -/// assert_eq!(iter.next().unwrap(), ("a", "1")); -/// assert_eq!(iter.next().unwrap(), ("b", "2")); -/// assert_eq!(iter.next().unwrap(), ("c", "3|4|5")); -/// assert_eq!(iter.next().unwrap(), ("d", "6")); -/// assert!(iter.next().is_none()); -/// -/// // Create properties from iterators -/// let pi = Properties::from_iter(vec![("a", "1"), ("b", "2"), ("c", "3|4|5"), ("d", "6")]); -/// assert_eq!(p, pi); -/// ``` -#[derive(Clone, PartialEq, Eq, Hash, Default)] -pub struct Properties<'s>(Cow<'s, str>); - -impl<'s> Properties<'s> { - /// Returns `true` if properties does not contain anything. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Returns properties as [`str`]. - pub fn as_str(&'s self) -> &'s str { - &self.0 - } - - /// Returns `true` if properties contains the specified key. - pub fn contains_key(&self, k: K) -> bool - where - K: Borrow, - { - Parameters::get(self.as_str(), k.borrow()).is_some() - } - - /// Returns a reference to the `&str`-value corresponding to the key. - pub fn get(&'s self, k: K) -> Option<&'s str> - where - K: Borrow, - { - Parameters::get(self.as_str(), k.borrow()) - } - - /// Returns an iterator to the `&str`-values corresponding to the key. - pub fn values(&'s self, k: K) -> impl DoubleEndedIterator - where - K: Borrow, - { - Parameters::values(self.as_str(), k.borrow()) - } - - /// Returns an iterator on the key-value pairs as `(&str, &str)`. - pub fn iter(&'s self) -> impl DoubleEndedIterator + Clone { - Parameters::iter(self.as_str()) - } - - /// Inserts a key-value pair into the map. - /// If the map did not have this key present, [`None`]` is returned. - /// If the map did have this key present, the value is updated, and the old value is returned. - pub fn insert(&mut self, k: K, v: V) -> Option - where - K: Borrow, - V: Borrow, - { - let (inner, item) = Parameters::insert(self.as_str(), k.borrow(), v.borrow()); - let item = item.map(|i| i.to_string()); - self.0 = Cow::Owned(inner); - item - } - - /// Removes a key from the map, returning the value at the key if the key was previously in the properties. - pub fn remove(&mut self, k: K) -> Option - where - K: Borrow, - { - let (inner, item) = Parameters::remove(self.as_str(), k.borrow()); - let item = item.map(|i| i.to_string()); - self.0 = Cow::Owned(inner); - item - } - - /// Extend these properties with other properties. - pub fn extend(&mut self, other: &Properties) { - self.extend_from_iter(other.iter()); - } - - /// Extend these properties from an iterator. - pub fn extend_from_iter<'e, I, K, V>(&mut self, iter: I) - where - I: Iterator + Clone, - K: Borrow + 'e + ?Sized, - V: Borrow + 'e + ?Sized, - { - let inner = Parameters::from_iter(Parameters::join( - self.iter(), - iter.map(|(k, v)| (k.borrow(), v.borrow())), - )); - self.0 = Cow::Owned(inner); - } - - /// Convert these properties into owned properties. - pub fn into_owned(self) -> Properties<'static> { - Properties(Cow::Owned(self.0.into_owned())) - } - - /// Returns `true`` if all keys are sorted in alphabetical order. - pub fn is_ordered(&self) -> bool { - Parameters::is_ordered(self.as_str()) - } -} - -impl<'s> From<&'s str> for Properties<'s> { - fn from(mut value: &'s str) -> Self { - value = value.trim_end_matches(|c| { - c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR - }); - Self(Cow::Borrowed(value)) - } -} - -impl From for Properties<'_> { - fn from(mut value: String) -> Self { - let s = value.trim_end_matches(|c| { - c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR - }); - value.truncate(s.len()); - Self(Cow::Owned(value)) - } -} - -impl<'s> From> for Properties<'s> { - fn from(value: Cow<'s, str>) -> Self { - match value { - Cow::Borrowed(s) => Properties::from(s), - Cow::Owned(s) => Properties::from(s), - } - } -} - -impl<'s, K, V> FromIterator<(&'s K, &'s V)> for Properties<'_> -where - K: Borrow + 's + ?Sized, - V: Borrow + 's + ?Sized, -{ - fn from_iter>(iter: T) -> Self { - let iter = iter.into_iter(); - let inner = Parameters::from_iter(iter.map(|(k, v)| (k.borrow(), v.borrow()))); - Self(Cow::Owned(inner)) - } -} - -impl<'s, K, V> FromIterator<&'s (K, V)> for Properties<'_> -where - K: Borrow + 's, - V: Borrow + 's, -{ - fn from_iter>(iter: T) -> Self { - Self::from_iter(iter.into_iter().map(|(k, v)| (k.borrow(), v.borrow()))) - } -} - -impl<'s, K, V> From<&'s [(K, V)]> for Properties<'_> -where - K: Borrow + 's, - V: Borrow + 's, -{ - fn from(value: &'s [(K, V)]) -> Self { - Self::from_iter(value.iter()) - } -} - -#[cfg(feature = "std")] -impl From> for Properties<'_> -where - K: Borrow, - V: Borrow, -{ - fn from(map: HashMap) -> Self { - Self::from_iter(map.iter()) - } -} - -#[cfg(feature = "std")] -impl<'s> From<&'s Properties<'s>> for HashMap<&'s str, &'s str> { - fn from(props: &'s Properties<'s>) -> Self { - HashMap::from_iter(props.iter()) - } -} - -#[cfg(feature = "std")] -impl From<&Properties<'_>> for HashMap { - fn from(props: &Properties<'_>) -> Self { - HashMap::from_iter(props.iter().map(|(k, v)| (k.to_string(), v.to_string()))) - } -} - -#[cfg(feature = "std")] -impl<'s> From<&'s Properties<'s>> for HashMap, Cow<'s, str>> { - fn from(props: &'s Properties<'s>) -> Self { - HashMap::from_iter(props.iter().map(|(k, v)| (Cow::from(k), Cow::from(v)))) - } -} - -#[cfg(feature = "std")] -impl From> for HashMap { - fn from(props: Properties) -> Self { - HashMap::from(&props) - } -} - -impl fmt::Display for Properties<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl fmt::Debug for Properties<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self) - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Default)] -pub struct OrderedProperties<'s>(Properties<'s>); - -impl<'s> OrderedProperties<'s> { - /// Returns `true` if properties does not contain anything. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Returns properties as [`str`]. - pub fn as_str(&'s self) -> &'s str { - self.0.as_str() - } - - /// Returns `true` if properties contains the specified key. - pub fn contains_key(&self, k: K) -> bool - where - K: Borrow, - { - self.0.contains_key(k) - } - - /// Returns a reference to the `&str`-value corresponding to the key. - pub fn get(&'s self, k: K) -> Option<&'s str> - where - K: Borrow, - { - self.0.get(k) - } - - /// Returns an iterator to the `&str`-values corresponding to the key. - pub fn values(&'s self, k: K) -> impl DoubleEndedIterator - where - K: Borrow, - { - self.0.values(k) - } - - /// Returns an iterator on the key-value pairs as `(&str, &str)`. - pub fn iter(&'s self) -> impl DoubleEndedIterator + Clone { - self.0.iter() - } - - /// Removes a key from the map, returning the value at the key if the key was previously in the properties. - pub fn remove(&mut self, k: K) -> Option - where - K: Borrow, - { - self.0.remove(k) - } - - /// Inserts a key-value pair into the map. - /// If the map did not have this key present, [`None`]` is returned. - /// If the map did have this key present, the value is updated, and the old value is returned. - pub fn insert(&mut self, k: K, v: V) -> Option - where - K: Borrow, - V: Borrow, - { - let item = self.0.insert(k, v); - self.order(); - item - } - - /// Extend these properties with other properties. - pub fn extend(&mut self, other: &Properties) { - self.extend_from_iter(other.iter()); - } - - /// Extend these properties from an iterator. - pub fn extend_from_iter<'e, I, K, V>(&mut self, iter: I) - where - I: Iterator + Clone, - K: Borrow + 'e + ?Sized, - V: Borrow + 'e + ?Sized, - { - self.0.extend_from_iter(iter); - self.order(); - } - - /// Convert these properties into owned properties. - pub fn into_owned(self) -> OrderedProperties<'static> { - OrderedProperties(self.0.into_owned()) - } - - fn order(&mut self) { - if !self.0.is_ordered() { - self.0 = Properties(Cow::Owned(Parameters::from_iter(Parameters::sort( - self.iter(), - )))); - } - } -} - -impl<'s> From> for OrderedProperties<'s> { - fn from(value: Properties<'s>) -> Self { - let mut props = Self(value); - props.order(); - props - } -} - -impl<'s> From<&'s str> for OrderedProperties<'s> { - fn from(value: &'s str) -> Self { - Self::from(Properties::from(value)) - } -} - -impl From for OrderedProperties<'_> { - fn from(value: String) -> Self { - Self::from(Properties::from(value)) - } -} - -impl<'s> From> for OrderedProperties<'s> { - fn from(value: Cow<'s, str>) -> Self { - Self::from(Properties::from(value)) - } -} - -impl<'s, K, V> FromIterator<(&'s K, &'s V)> for OrderedProperties<'_> -where - K: Borrow + 's + ?Sized, - V: Borrow + 's + ?Sized, -{ - fn from_iter>(iter: T) -> Self { - Self::from(Properties::from_iter(iter)) - } -} - -impl<'s, K, V> FromIterator<&'s (K, V)> for OrderedProperties<'_> -where - K: Borrow + 's, - V: Borrow + 's, -{ - fn from_iter>(iter: T) -> Self { - Self::from(Properties::from_iter(iter)) - } -} - -impl<'s, K, V> From<&'s [(K, V)]> for OrderedProperties<'_> -where - K: Borrow + 's, - V: Borrow + 's, -{ - fn from(value: &'s [(K, V)]) -> Self { - Self::from_iter(value.iter()) - } -} - -#[cfg(feature = "std")] -impl From> for OrderedProperties<'_> -where - K: Borrow, - V: Borrow, -{ - fn from(map: HashMap) -> Self { - Self::from_iter(map.iter()) - } -} - -#[cfg(feature = "std")] -impl<'s> From<&'s OrderedProperties<'s>> for HashMap<&'s str, &'s str> { - fn from(props: &'s OrderedProperties<'s>) -> Self { - HashMap::from(&props.0) - } -} - -#[cfg(feature = "std")] -impl From<&OrderedProperties<'_>> for HashMap { - fn from(props: &OrderedProperties<'_>) -> Self { - HashMap::from(&props.0) - } -} - -#[cfg(feature = "std")] -impl<'s> From<&'s OrderedProperties<'s>> for HashMap, Cow<'s, str>> { - fn from(props: &'s OrderedProperties<'s>) -> Self { - HashMap::from(&props.0) - } -} - -#[cfg(feature = "std")] -impl From> for HashMap { - fn from(props: OrderedProperties) -> Self { - HashMap::from(&props) - } -} - -impl fmt::Display for OrderedProperties<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl fmt::Debug for OrderedProperties<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_properties() { - assert!(Properties::from("").0.is_empty()); - - assert_eq!(Properties::from("p1"), Properties::from(&[("p1", "")][..])); - - assert_eq!( - Properties::from("p1=v1"), - Properties::from(&[("p1", "v1")][..]) - ); - - assert_eq!( - Properties::from("p1=v1;p2=v2;"), - Properties::from(&[("p1", "v1"), ("p2", "v2")][..]) - ); - - assert_eq!( - Properties::from("p1=v1;p2=v2;|="), - Properties::from(&[("p1", "v1"), ("p2", "v2")][..]) - ); - - assert_eq!( - Properties::from("p1=v1;p2;p3=v3"), - Properties::from(&[("p1", "v1"), ("p2", ""), ("p3", "v3")][..]) - ); - - assert_eq!( - Properties::from("p1=v 1;p 2=v2"), - Properties::from(&[("p1", "v 1"), ("p 2", "v2")][..]) - ); - - assert_eq!( - Properties::from("p1=x=y;p2=a==b"), - Properties::from(&[("p1", "x=y"), ("p2", "a==b")][..]) - ); - - let mut hm: HashMap = HashMap::new(); - hm.insert("p1".to_string(), "v1".to_string()); - assert_eq!(Properties::from(hm), Properties::from("p1=v1")); - - let mut hm: HashMap<&str, &str> = HashMap::new(); - hm.insert("p1", "v1"); - assert_eq!(Properties::from(hm), Properties::from("p1=v1")); - - let mut hm: HashMap, Cow> = HashMap::new(); - hm.insert(Cow::from("p1"), Cow::from("v1")); - assert_eq!(Properties::from(hm), Properties::from("p1=v1")); - } -} diff --git a/examples/examples/z_get_shm.rs b/examples/examples/z_get_shm.rs index 942ec0e34e..8766d54b95 100644 --- a/examples/examples/z_get_shm.rs +++ b/examples/examples/z_get_shm.rs @@ -17,7 +17,7 @@ use clap::Parser; use zenoh::{ prelude::*, query::QueryTarget, - selector::Selector, + selector::KeyExpr, shm::{ zshm, BlockOn, GarbageCollect, PosixSharedMemoryProviderBackend, SharedMemoryProviderBuilder, POSIX_PROTOCOL_ID, @@ -113,7 +113,7 @@ enum Qt { struct Args { #[arg(short, long, default_value = "demo/example/**")] /// The selection of resources to query - selector: Selector<'static>, + selector: KeyExpr<'static>, /// The value to publish. value: Option, #[arg(short, long, default_value = "BEST_MATCHING")] @@ -128,7 +128,7 @@ struct Args { fn parse_args() -> ( Config, - Selector<'static>, + KeyExpr<'static>, Option, QueryTarget, Duration, diff --git a/examples/examples/z_storage.rs b/examples/examples/z_storage.rs index 83a2dee66d..f812c78094 100644 --- a/examples/examples/z_storage.rs +++ b/examples/examples/z_storage.rs @@ -64,7 +64,7 @@ async fn main() { let query = query.unwrap(); println!(">> [Queryable ] Received Query '{}'", query.selector()); for (stored_name, sample) in stored.iter() { - if query.selector().key_expr().intersects(unsafe {keyexpr::from_str_unchecked(stored_name)}) { + if query.key_expr().intersects(unsafe {keyexpr::from_str_unchecked(stored_name)}) { query.reply(sample.key_expr().clone(), sample.payload().clone()).await.unwrap(); } } diff --git a/io/zenoh-links/zenoh-link-quic/src/utils.rs b/io/zenoh-links/zenoh-link-quic/src/utils.rs index 1eb8f94380..059734f9c9 100644 --- a/io/zenoh-links/zenoh-link-quic/src/utils.rs +++ b/io/zenoh-links/zenoh-link-quic/src/utils.rs @@ -31,7 +31,7 @@ use zenoh_config::Config as ZenohConfig; use zenoh_link_commons::ConfigurationInspector; use zenoh_protocol::core::{ endpoint::{Address, Config}, - Parameters, + ParametersView, }; use zenoh_result::{bail, zerror, ZError, ZResult}; @@ -140,7 +140,7 @@ impl ConfigurationInspector for TlsConfigurator { }; } - Ok(Parameters::from_iter(ps.drain(..))) + Ok(ParametersView::from_iter(ps.drain(..))) } } diff --git a/io/zenoh-links/zenoh-link-tls/src/utils.rs b/io/zenoh-links/zenoh-link-tls/src/utils.rs index b646c6e80d..1acaa05454 100644 --- a/io/zenoh-links/zenoh-link-tls/src/utils.rs +++ b/io/zenoh-links/zenoh-link-tls/src/utils.rs @@ -33,7 +33,7 @@ use zenoh_config::Config as ZenohConfig; use zenoh_link_commons::{tls::WebPkiVerifierAnyServerName, ConfigurationInspector}; use zenoh_protocol::core::{ endpoint::{Address, Config}, - Parameters, + ParametersView, }; use zenoh_result::{bail, zerror, ZError, ZResult}; @@ -142,7 +142,7 @@ impl ConfigurationInspector for TlsConfigurator { }; } - Ok(Parameters::from_iter(ps.drain(..))) + Ok(ParametersView::from_iter(ps.drain(..))) } } diff --git a/io/zenoh-links/zenoh-link-unixpipe/src/unix/mod.rs b/io/zenoh-links/zenoh-link-unixpipe/src/unix/mod.rs index 61c891da33..6d11878409 100644 --- a/io/zenoh-links/zenoh-link-unixpipe/src/unix/mod.rs +++ b/io/zenoh-links/zenoh-link-unixpipe/src/unix/mod.rs @@ -24,7 +24,7 @@ pub use unicast::*; use zenoh_config::Config; use zenoh_core::zconfigurable; use zenoh_link_commons::{ConfigurationInspector, LocatorInspector}; -use zenoh_protocol::core::{Locator, Parameters}; +use zenoh_protocol::core::{Locator, ParametersView}; use zenoh_result::ZResult; pub const UNIXPIPE_LOCATOR_PREFIX: &str = "unixpipe"; @@ -56,7 +56,7 @@ impl ConfigurationInspector for UnixPipeConfigurator { properties.push((config::FILE_ACCESS_MASK, &file_access_mask_)); } - let s = Parameters::from_iter(properties.drain(..)); + let s = ParametersView::from_iter(properties.drain(..)); Ok(s) } diff --git a/io/zenoh-transport/src/multicast/manager.rs b/io/zenoh-transport/src/multicast/manager.rs index 3c04cf6425..9e7ff1ea35 100644 --- a/io/zenoh-transport/src/multicast/manager.rs +++ b/io/zenoh-transport/src/multicast/manager.rs @@ -22,7 +22,7 @@ use zenoh_config::{Config, LinkTxConf}; use zenoh_core::zasynclock; use zenoh_link::*; use zenoh_protocol::{ - core::{Parameters, ZenohId}, + core::{ParametersView, ZenohId}, transport::close, }; use zenoh_result::{bail, zerror, ZResult}; @@ -258,7 +258,7 @@ impl TransportManager { if let Some(config) = self.config.endpoints.get(endpoint.protocol().as_str()) { endpoint .config_mut() - .extend_from_iter(Parameters::iter(config))?; + .extend_from_iter(ParametersView::iter(config))?; } // Open the link diff --git a/io/zenoh-transport/src/unicast/manager.rs b/io/zenoh-transport/src/unicast/manager.rs index f42002b0d3..89ecc1cb1c 100644 --- a/io/zenoh-transport/src/unicast/manager.rs +++ b/io/zenoh-transport/src/unicast/manager.rs @@ -30,7 +30,7 @@ use zenoh_core::{zasynclock, zcondfeat}; use zenoh_crypto::PseudoRng; use zenoh_link::*; use zenoh_protocol::{ - core::{Parameters, ZenohId}, + core::{ParametersView, ZenohId}, transport::{close, TransportSn}, }; use zenoh_result::{bail, zerror, ZResult}; @@ -387,7 +387,7 @@ impl TransportManager { if let Some(config) = self.config.endpoints.get(endpoint.protocol().as_str()) { endpoint .config_mut() - .extend_from_iter(Parameters::iter(config))?; + .extend_from_iter(ParametersView::iter(config))?; }; manager.new_listener(endpoint).await } @@ -698,7 +698,7 @@ impl TransportManager { if let Some(config) = self.config.endpoints.get(endpoint.protocol().as_str()) { endpoint .config_mut() - .extend_from_iter(Parameters::iter(config))?; + .extend_from_iter(ParametersView::iter(config))?; }; // Create a new link associated by calling the Link Manager diff --git a/plugins/zenoh-plugin-example/src/lib.rs b/plugins/zenoh-plugin-example/src/lib.rs index d6c58bed0b..4c55b415af 100644 --- a/plugins/zenoh-plugin-example/src/lib.rs +++ b/plugins/zenoh-plugin-example/src/lib.rs @@ -183,7 +183,7 @@ async fn run(runtime: Runtime, selector: KeyExpr<'_>, flag: Arc) { let query = query.unwrap(); info!("Handling query '{}'", query.selector()); for (key_expr, sample) in stored.iter() { - if query.selector().key_expr().intersects(unsafe{keyexpr::from_str_unchecked(key_expr)}) { + if query.key_expr().intersects(unsafe{keyexpr::from_str_unchecked(key_expr)}) { query.reply_sample(sample.clone()).await.unwrap(); } } diff --git a/plugins/zenoh-plugin-rest/src/lib.rs b/plugins/zenoh-plugin-rest/src/lib.rs index 4f0ca3f67d..072a060d1a 100644 --- a/plugins/zenoh-plugin-rest/src/lib.rs +++ b/plugins/zenoh-plugin-rest/src/lib.rs @@ -35,7 +35,7 @@ use zenoh::{ key_expr::{keyexpr, KeyExpr}, query::{QueryConsolidation, Reply}, sample::{Sample, SampleKind, ValueBuilderTrait}, - selector::{Selector, TIME_RANGE_KEY}, + selector::{Parameters, Selector, TIME_RANGE_KEY}, session::{Session, SessionDeclarations}, value::Value, }; @@ -252,16 +252,13 @@ impl PluginControl for RunningPlugin {} impl RunningPluginTrait for RunningPlugin { fn adminspace_getter<'a>( &'a self, - selector: &'a Selector<'a>, + key_expr: &'a KeyExpr<'a>, plugin_status_key: &str, ) -> ZResult> { let mut responses = Vec::new(); let mut key = String::from(plugin_status_key); with_extended_string(&mut key, &["/version"], |key| { - if keyexpr::new(key.as_str()) - .unwrap() - .intersects(selector.key_expr()) - { + if keyexpr::new(key.as_str()).unwrap().intersects(key_expr) { responses.push(zenoh::internal::plugins::Response::new( key.clone(), GIT_VERSION.into(), @@ -271,7 +268,7 @@ impl RunningPluginTrait for RunningPlugin { with_extended_string(&mut key, &["/port"], |port_key| { if keyexpr::new(port_key.as_str()) .unwrap() - .intersects(selector.key_expr()) + .intersects(key_expr) { responses.push(zenoh::internal::plugins::Response::new( port_key.clone(), @@ -385,18 +382,18 @@ async fn query(mut req: Request<(Arc, String)>) -> tide::Result( &'a self, - selector: &'a Selector<'a>, + key_expr: &'a KeyExpr<'a>, plugin_status_key: &str, ) -> ZResult> { let mut responses = Vec::new(); let mut key = String::from(plugin_status_key); // TODO: to be removed when "__version__" is implemented in admoin space with_extended_string(&mut key, &["/version"], |key| { - if keyexpr::new(key.as_str()) - .unwrap() - .intersects(selector.key_expr()) - { + if keyexpr::new(key.as_str()).unwrap().intersects(key_expr) { responses.push(Response::new( key.clone(), StoragesPlugin::PLUGIN_VERSION.into(), @@ -327,17 +323,11 @@ impl RunningPluginTrait for StorageRuntime { for plugin in guard.plugins_manager.started_plugins_iter() { with_extended_string(key, &[plugin.id()], |key| { with_extended_string(key, &["/__path__"], |key| { - if keyexpr::new(key.as_str()) - .unwrap() - .intersects(selector.key_expr()) - { + if keyexpr::new(key.as_str()).unwrap().intersects(key_expr) { responses.push(Response::new(key.clone(), plugin.path().into())) } }); - if keyexpr::new(key.as_str()) - .unwrap() - .intersects(selector.key_expr()) - { + if keyexpr::new(key.as_str()).unwrap().intersects(key_expr) { responses.push(Response::new( key.clone(), plugin.instance().get_admin_status(), @@ -350,10 +340,7 @@ impl RunningPluginTrait for StorageRuntime { for storages in guard.storages.values() { for (storage, handle) in storages { with_extended_string(key, &[storage], |key| { - if keyexpr::new(key.as_str()) - .unwrap() - .intersects(selector.key_expr()) - { + if keyexpr::new(key.as_str()).unwrap().intersects(key_expr) { if let Ok(value) = task::block_on(async { let (tx, rx) = async_std::channel::bounded(1); let _ = handle.send(StorageMessage::GetStatus(tx)); diff --git a/plugins/zenoh-plugin-storage-manager/src/replica/align_queryable.rs b/plugins/zenoh-plugin-storage-manager/src/replica/align_queryable.rs index 66233d2535..30a40abe30 100644 --- a/plugins/zenoh-plugin-storage-manager/src/replica/align_queryable.rs +++ b/plugins/zenoh-plugin-storage-manager/src/replica/align_queryable.rs @@ -20,8 +20,8 @@ use std::{ use async_std::sync::Arc; use zenoh::{ - bytes::StringOrBase64, key_expr::OwnedKeyExpr, prelude::*, sample::Sample, selector::Selector, - time::Timestamp, value::Value, Session, + bytes::StringOrBase64, key_expr::OwnedKeyExpr, prelude::*, sample::Sample, + selector::Parameters, time::Timestamp, value::Value, Session, }; use super::{digest::*, Snapshotter}; @@ -86,7 +86,7 @@ impl AlignQueryable { } }; tracing::trace!("[ALIGN QUERYABLE] Received Query '{}'", query.selector()); - let diff_required = self.parse_selector(query.selector()); + let diff_required = self.parse_parameters(query.parameters()); tracing::trace!( "[ALIGN QUERYABLE] Parsed selector diff_required:{:?}", diff_required @@ -187,15 +187,14 @@ impl AlignQueryable { } } - fn parse_selector(&self, selector: Selector) -> Option { - let properties = selector.parameters(); // note: this is a hashmap - tracing::trace!("[ALIGN QUERYABLE] Properties are: {:?}", properties); - if properties.contains_key(super::ERA) { + fn parse_parameters(&self, parameters: &Parameters) -> Option { + tracing::trace!("[ALIGN QUERYABLE] Properties are: {:?}", parameters); + if parameters.contains_key(super::ERA) { Some(AlignComponent::Era( - EraType::from_str(properties.get(super::ERA).unwrap()).unwrap(), + EraType::from_str(parameters.get(super::ERA).unwrap()).unwrap(), )) - } else if properties.contains_key(super::INTERVALS) { - let mut intervals = properties.get(super::INTERVALS).unwrap().to_string(); + } else if parameters.contains_key(super::INTERVALS) { + let mut intervals = parameters.get(super::INTERVALS).unwrap().to_string(); intervals.remove(0); intervals.pop(); Some(AlignComponent::Intervals( @@ -204,8 +203,8 @@ impl AlignQueryable { .map(|x| x.parse::().unwrap()) .collect::>(), )) - } else if properties.contains_key(super::SUBINTERVALS) { - let mut subintervals = properties.get(super::SUBINTERVALS).unwrap().to_string(); + } else if parameters.contains_key(super::SUBINTERVALS) { + let mut subintervals = parameters.get(super::SUBINTERVALS).unwrap().to_string(); subintervals.remove(0); subintervals.pop(); Some(AlignComponent::Subintervals( @@ -214,8 +213,8 @@ impl AlignQueryable { .map(|x| x.parse::().unwrap()) .collect::>(), )) - } else if properties.contains_key(super::CONTENTS) { - let contents = serde_json::from_str(properties.get(super::CONTENTS).unwrap()).unwrap(); + } else if parameters.contains_key(super::CONTENTS) { + let contents = serde_json::from_str(parameters.get(super::CONTENTS).unwrap()).unwrap(); Some(AlignComponent::Contents(contents)) } else { None diff --git a/plugins/zenoh-plugin-storage-manager/src/replica/aligner.rs b/plugins/zenoh-plugin-storage-manager/src/replica/aligner.rs index eaecee5246..c20f074e1b 100644 --- a/plugins/zenoh-plugin-storage-manager/src/replica/aligner.rs +++ b/plugins/zenoh-plugin-storage-manager/src/replica/aligner.rs @@ -323,7 +323,7 @@ impl Aligner { async fn perform_query(&self, from: &str, properties: String) -> (Vec, bool) { let mut no_err = true; - let selector = Selector::new( + let selector = Selector::owned( KeyExpr::from(&self.digest_key).join(&from).unwrap(), properties, ); diff --git a/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs b/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs index 4087fb3682..ed7f533147 100644 --- a/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs +++ b/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs @@ -647,7 +647,7 @@ impl StorageService { // with `_time=[..]` to get historical data (in case of time-series) let replies = match self .session - .get(Selector::new(&self.key_expr, "_time=[..]")) + .get(Selector::owned(&self.key_expr, "_time=[..]")) .target(QueryTarget::All) .consolidation(ConsolidationMode::None) .await diff --git a/zenoh-ext/src/publication_cache.rs b/zenoh-ext/src/publication_cache.rs index dc01367666..6f7548d97c 100644 --- a/zenoh-ext/src/publication_cache.rs +++ b/zenoh-ext/src/publication_cache.rs @@ -26,6 +26,7 @@ use zenoh::{ query::Query, queryable::Queryable, sample::{Locality, Sample}, + selector::PredefinedParameters, session::{SessionDeclarations, SessionRef}, subscriber::FlumeSubscriber, }; @@ -212,8 +213,8 @@ impl<'a> PublicationCache<'a> { // on query, reply with cache content query = quer_recv.recv_async() => { if let Ok(query) = query { - if !query.selector().key_expr().as_str().contains('*') { - if let Some(queue) = cache.get(query.selector().key_expr().as_keyexpr()) { + if !query.key_expr().as_str().contains('*') { + if let Some(queue) = cache.get(query.key_expr().as_keyexpr()) { for sample in queue { if let (Ok(Some(time_range)), Some(timestamp)) = (query.parameters().time_range(), sample.timestamp()) { if !time_range.contains(timestamp.get_time().to_system_time()){ @@ -227,7 +228,7 @@ impl<'a> PublicationCache<'a> { } } else { for (key_expr, queue) in cache.iter() { - if query.selector().key_expr().intersects(unsafe{ keyexpr::from_str_unchecked(key_expr) }) { + if query.key_expr().intersects(unsafe{ keyexpr::from_str_unchecked(key_expr) }) { for sample in queue { if let (Ok(Some(time_range)), Some(timestamp)) = (query.parameters().time_range(), sample.timestamp()) { if !time_range.contains(timestamp.get_time().to_system_time()){ diff --git a/zenoh-ext/src/querying_subscriber.rs b/zenoh-ext/src/querying_subscriber.rs index 54f3ff0224..2adf4d43ae 100644 --- a/zenoh-ext/src/querying_subscriber.rs +++ b/zenoh-ext/src/querying_subscriber.rs @@ -28,7 +28,6 @@ use zenoh::{ prelude::Wait, query::{QueryConsolidation, QueryTarget, ReplyKeyExpr}, sample::{Locality, Sample, SampleBuilder, TimestampBuilderTrait}, - selector::Selector, session::{SessionDeclarations, SessionRef}, subscriber::{Reliability, Subscriber}, time::{new_timestamp, Timestamp}, @@ -44,7 +43,7 @@ pub struct QueryingSubscriberBuilder<'a, 'b, KeySpace, Handler> { pub(crate) key_space: KeySpace, pub(crate) reliability: Reliability, pub(crate) origin: Locality, - pub(crate) query_selector: Option>>, + pub(crate) query_selector: Option>>, pub(crate) query_target: QueryTarget, pub(crate) query_consolidation: QueryConsolidation, pub(crate) query_accept_replies: ReplyKeyExpr, @@ -179,8 +178,8 @@ impl<'a, 'b, Handler> QueryingSubscriberBuilder<'a, 'b, crate::UserSpace, Handle #[inline] pub fn query_selector(mut self, query_selector: IntoSelector) -> Self where - IntoSelector: TryInto>, - >>::Error: Into, + IntoSelector: TryInto>, + >>::Error: Into, { self.query_selector = Some(query_selector.try_into().map_err(Into::into)); self diff --git a/zenoh/src/api/bytes.rs b/zenoh/src/api/bytes.rs index c4bcf6ae5e..920be0bbaa 100644 --- a/zenoh/src/api/bytes.rs +++ b/zenoh/src/api/bytes.rs @@ -26,7 +26,7 @@ use zenoh_buffers::{ ZBuf, ZBufReader, ZBufWriter, ZSlice, }; use zenoh_codec::{RCodec, WCodec, Zenoh080}; -use zenoh_protocol::{core::Properties, zenoh::ext::AttachmentType}; +use zenoh_protocol::{core::Parameters, zenoh::ext::AttachmentType}; use zenoh_result::{ZError, ZResult}; #[cfg(feature = "shared-memory")] use zenoh_shm::{ @@ -1145,70 +1145,70 @@ impl TryFrom<&mut ZBytes> for bool { // - Zenoh advanced types encoders/decoders // Properties -impl Serialize> for ZSerde { +impl Serialize> for ZSerde { type Output = ZBytes; - fn serialize(self, t: Properties<'_>) -> Self::Output { + fn serialize(self, t: Parameters<'_>) -> Self::Output { Self.serialize(t.as_str()) } } -impl From> for ZBytes { - fn from(t: Properties<'_>) -> Self { +impl From> for ZBytes { + fn from(t: Parameters<'_>) -> Self { ZSerde.serialize(t) } } -impl Serialize<&Properties<'_>> for ZSerde { +impl Serialize<&Parameters<'_>> for ZSerde { type Output = ZBytes; - fn serialize(self, t: &Properties<'_>) -> Self::Output { + fn serialize(self, t: &Parameters<'_>) -> Self::Output { Self.serialize(t.as_str()) } } -impl<'s> From<&'s Properties<'s>> for ZBytes { - fn from(t: &'s Properties<'s>) -> Self { +impl<'s> From<&'s Parameters<'s>> for ZBytes { + fn from(t: &'s Parameters<'s>) -> Self { ZSerde.serialize(t) } } -impl Serialize<&mut Properties<'_>> for ZSerde { +impl Serialize<&mut Parameters<'_>> for ZSerde { type Output = ZBytes; - fn serialize(self, t: &mut Properties<'_>) -> Self::Output { + fn serialize(self, t: &mut Parameters<'_>) -> Self::Output { Self.serialize(t.as_str()) } } -impl<'s> From<&'s mut Properties<'s>> for ZBytes { - fn from(t: &'s mut Properties<'s>) -> Self { +impl<'s> From<&'s mut Parameters<'s>> for ZBytes { + fn from(t: &'s mut Parameters<'s>) -> Self { ZSerde.serialize(&*t) } } -impl<'s> Deserialize<'s, Properties<'s>> for ZSerde { +impl<'s> Deserialize<'s, Parameters<'s>> for ZSerde { type Input = &'s ZBytes; type Error = ZDeserializeError; - fn deserialize(self, v: Self::Input) -> Result, Self::Error> { + fn deserialize(self, v: Self::Input) -> Result, Self::Error> { let s = v .deserialize::>() .map_err(|_| ZDeserializeError)?; - Ok(Properties::from(s)) + Ok(Parameters::from(s)) } } -impl TryFrom for Properties<'static> { +impl TryFrom for Parameters<'static> { type Error = ZDeserializeError; fn try_from(v: ZBytes) -> Result { let s = v.deserialize::>().map_err(|_| ZDeserializeError)?; - Ok(Properties::from(s.into_owned())) + Ok(Parameters::from(s.into_owned())) } } -impl<'s> TryFrom<&'s ZBytes> for Properties<'s> { +impl<'s> TryFrom<&'s ZBytes> for Parameters<'s> { type Error = ZDeserializeError; fn try_from(value: &'s ZBytes) -> Result { @@ -1216,7 +1216,7 @@ impl<'s> TryFrom<&'s ZBytes> for Properties<'s> { } } -impl<'s> TryFrom<&'s mut ZBytes> for Properties<'s> { +impl<'s> TryFrom<&'s mut ZBytes> for Parameters<'s> { type Error = ZDeserializeError; fn try_from(value: &'s mut ZBytes) -> Result { @@ -1877,7 +1877,7 @@ mod tests { use zenoh_buffers::{ZBuf, ZSlice}; #[cfg(feature = "shared-memory")] use zenoh_core::Wait; - use zenoh_protocol::core::Properties; + use zenoh_protocol::core::Parameters; #[cfg(feature = "shared-memory")] use zenoh_shm::api::{ buffer::zshm::{zshm, ZShm}, @@ -2018,9 +2018,9 @@ mod tests { serialize_deserialize!(&zshm, immutable_shm_buf); } - // Properties - serialize_deserialize!(Properties, Properties::from("")); - serialize_deserialize!(Properties, Properties::from("a=1;b=2;c3")); + // Parameters + serialize_deserialize!(Parameters, Parameters::from("")); + serialize_deserialize!(Parameters, Parameters::from("a=1;b=2;c3")); // Tuple serialize_deserialize!((usize, usize), (0, 1)); diff --git a/zenoh/src/api/key_expr.rs b/zenoh/src/api/key_expr.rs index 8215fe5278..18b3e2ca0c 100644 --- a/zenoh/src/api/key_expr.rs +++ b/zenoh/src/api/key_expr.rs @@ -358,6 +358,7 @@ impl<'a> From> for String { } } } + impl<'a> TryFrom for KeyExpr<'a> { type Error = zenoh_result::Error; fn try_from(value: String) -> Result { diff --git a/zenoh/src/api/liveliness.rs b/zenoh/src/api/liveliness.rs index 5011b99a7e..f100a7469c 100644 --- a/zenoh/src/api/liveliness.rs +++ b/zenoh/src/api/liveliness.rs @@ -22,7 +22,10 @@ use std::{ use zenoh_config::unwrap_or_default; use zenoh_core::{Resolvable, Resolve, Result as ZResult, Wait}; use zenoh_keyexpr::keyexpr; -use zenoh_protocol::network::{declare::subscriber::ext::SubscriberInfo, request}; +use zenoh_protocol::{ + core::Parameters, + network::{declare::subscriber::ext::SubscriberInfo, request}, +}; use super::{ handlers::{locked, DefaultHandler, IntoHandler}, @@ -743,6 +746,7 @@ where self.session .query( &self.key_expr?.into(), + &Parameters::default(), &Some(KeyExpr::from(*KE_PREFIX_LIVELINESS)), QueryTarget::DEFAULT, QueryConsolidation::DEFAULT, diff --git a/zenoh/src/api/plugins.rs b/zenoh/src/api/plugins.rs index b7f1954a6b..63519eac2b 100644 --- a/zenoh/src/api/plugins.rs +++ b/zenoh/src/api/plugins.rs @@ -21,7 +21,7 @@ use zenoh_plugin_trait::{ use zenoh_protocol::core::key_expr::keyexpr; use zenoh_result::ZResult; -use super::selector::Selector; +use super::key_expr::KeyExpr; use crate::net::runtime::Runtime; zconfigurable! { @@ -93,9 +93,9 @@ pub trait RunningPluginTrait: Send + Sync + PluginControl { /// Function called on any query on admin space that matches this plugin's sub-part of the admin space. /// Thus the plugin can reply its contribution to the global admin space of this zenohd. /// Parameters: - /// * `selector`: the full selector of the query (usually only key_expr part is used). This selector is + /// * `key_expr`: the key_expr selector of the query. This key_expr is /// exactly the same as it was requested by user, for example "@/router/ROUTER_ID/plugins/PLUGIN_NAME/some/plugin/info" or "@/router/*/plugins/*/foo/bar". - /// But the plugin's [RunningPluginTrait::adminspace_getter] is called only if the selector matches the `plugin_status_key` + /// But the plugin's [RunningPluginTrait::adminspace_getter] is called only if the key_expr matches the `plugin_status_key` /// * `plugin_status_key`: the actual path to plugin's status in the admin space. For example "@/router/ROUTER_ID/plugins/PLUGIN_NAME" /// Returns value: /// * `Ok(Vec)`: the list of responses to the query. For example if plugins can return information on subleys "foo", "bar", "foo/buzz" and "bar/buzz" @@ -113,7 +113,7 @@ pub trait RunningPluginTrait: Send + Sync + PluginControl { /// fn adminspace_getter<'a>( &'a self, - _selector: &'a Selector<'a>, + _key_expr: &'a KeyExpr<'a>, _plugin_status_key: &str, ) -> ZResult> { Ok(Vec::new()) diff --git a/zenoh/src/api/query.rs b/zenoh/src/api/query.rs index 562069566b..ba925876c9 100644 --- a/zenoh/src/api/query.rs +++ b/zenoh/src/api/query.rs @@ -18,9 +18,11 @@ use std::{ time::Duration, }; +#[zenoh_macros::unstable] +use std::borrow::Cow; use zenoh_core::{Resolvable, Wait}; use zenoh_keyexpr::OwnedKeyExpr; -use zenoh_protocol::core::{CongestionControl, ZenohId}; +use zenoh_protocol::core::{CongestionControl, Parameters, ZenohId}; use zenoh_result::ZResult; #[zenoh_macros::unstable] @@ -117,13 +119,20 @@ impl From for Result { pub(crate) struct QueryState { pub(crate) nb_final: usize, - pub(crate) selector: Selector<'static>, + pub(crate) key_expr: KeyExpr<'static>, + pub(crate) parameters: Parameters<'static>, pub(crate) scope: Option>, pub(crate) reception_mode: ConsolidationMode, pub(crate) replies: Option>, pub(crate) callback: Callback<'static, Reply>, } +impl QueryState { + pub(crate) fn selector(&self) -> Selector { + Selector::borrowed(&self.key_expr, &self.parameters) + } +} + /// A builder for initializing a `query`. /// /// # Examples @@ -407,12 +416,27 @@ impl<'a, 'b, Handler> SessionGetBuilder<'a, 'b, Handler> { #[zenoh_macros::unstable] pub fn accept_replies(self, accept: ReplyKeyExpr) -> Self { Self { - selector: self.selector.map(|mut s| { - if accept == ReplyKeyExpr::Any { - s.parameters_mut().insert(_REPLY_KEY_EXPR_ANY_SEL_PARAM, ""); - } - s - }), + selector: self.selector.map( + |Selector { + key_expr, + parameters, + }| { + if accept == ReplyKeyExpr::Any { + let mut parameters = parameters.into_owned(); + parameters.insert(_REPLY_KEY_EXPR_ANY_SEL_PARAM, ""); + let parameters = Cow::Owned(parameters); + Selector { + key_expr, + parameters, + } + } else { + Selector { + key_expr, + parameters, + } + } + }, + ), ..self } } @@ -445,10 +469,14 @@ where { fn wait(self) -> ::To { let (callback, receiver) = self.handler.into_handler(); - + let Selector { + key_expr, + parameters, + } = self.selector?; self.session .query( - &self.selector?, + &key_expr, + ¶meters, &self.scope?, self.target, self.consolidation, diff --git a/zenoh/src/api/queryable.rs b/zenoh/src/api/queryable.rs index bb41a37c2f..f113dfc11e 100644 --- a/zenoh/src/api/queryable.rs +++ b/zenoh/src/api/queryable.rs @@ -21,7 +21,7 @@ use std::{ use uhlc::Timestamp; use zenoh_core::{Resolvable, Resolve, Wait}; use zenoh_protocol::{ - core::{CongestionControl, EntityId, WireExpr, ZenohId}, + core::{CongestionControl, EntityId, Parameters, WireExpr, ZenohId}, network::{response, Mapping, RequestId, Response, ResponseFinal}, zenoh::{self, reply::ReplyBody, Del, Put, ResponseBody}, }; @@ -43,7 +43,7 @@ use super::{ key_expr::KeyExpr, publisher::Priority, sample::{Locality, QoSBuilder, Sample, SampleKind}, - selector::{Parameters, Selector}, + selector::Selector, session::{SessionRef, Undeclarable}, value::Value, Id, @@ -81,10 +81,7 @@ impl Query { /// The full [`Selector`] of this Query. #[inline(always)] pub fn selector(&self) -> Selector<'_> { - Selector { - key_expr: self.inner.key_expr.clone(), - parameters: self.inner.parameters.clone(), - } + Selector::borrowed(&self.inner.key_expr, &self.inner.parameters) } /// The key selector part of this Query. diff --git a/zenoh/src/api/selector.rs b/zenoh/src/api/selector.rs index 7477ea65e9..6f6eeaf8ff 100644 --- a/zenoh/src/api/selector.rs +++ b/zenoh/src/api/selector.rs @@ -13,16 +13,11 @@ // //! [Selector](https://github.com/eclipse-zenoh/roadmap/tree/main/rfcs/ALL/Selectors) to issue queries -use std::{ - collections::HashMap, - convert::TryFrom, - ops::{Deref, DerefMut}, - str::FromStr, -}; +use std::{borrow::Cow, convert::TryFrom, str::FromStr}; use zenoh_protocol::core::{ key_expr::{keyexpr, OwnedKeyExpr}, - Properties, + Parameters, }; #[cfg(feature = "unstable")] use zenoh_result::ZResult; @@ -60,7 +55,7 @@ use super::{key_expr::KeyExpr, queryable::Query}; /// queryables. /// /// Here are the currently standardized parameters for Zenoh (check the specification page for the exhaustive list): -/// - `_time`: used to express interest in only values dated within a certain time range, values for +/// - **`[unstable]`** `_time`: used to express interest in only values dated within a certain time range, values for /// this parameter must be readable by the [Zenoh Time DSL](zenoh_util::time_range::TimeRange) for the value to be considered valid. /// - **`[unstable]`** `_anyke`: used in queries to express interest in replies coming from any key expression. By default, only replies /// whose key expression match query's key expression are accepted. `_anyke` disables the query-reply key expression matching check. @@ -68,146 +63,76 @@ use super::{key_expr::KeyExpr, queryable::Query}; #[derive(Clone, PartialEq, Eq)] pub struct Selector<'a> { /// The part of this selector identifying which keys should be part of the selection. - pub(crate) key_expr: KeyExpr<'a>, + pub key_expr: Cow<'a, KeyExpr<'a>>, /// the part of this selector identifying which values should be part of the selection. - pub(crate) parameters: Parameters<'a>, + pub parameters: Cow<'a, Parameters<'a>>, } #[zenoh_macros::unstable] pub const TIME_RANGE_KEY: &str = "_time"; + impl<'a> Selector<'a> { - /// Builds a new selector - pub fn new(key_expr: K, parameters: P) -> Self + /// Builds a new selector which owns keyexpr and parameters + pub fn owned(key_expr: K, parameters: P) -> Self where K: Into>, P: Into>, { Self { - key_expr: key_expr.into(), - parameters: parameters.into(), + key_expr: Cow::Owned(key_expr.into()), + parameters: Cow::Owned(parameters.into()), } } - - /// Gets the key-expression. - pub fn key_expr(&'a self) -> &KeyExpr<'a> { - &self.key_expr - } - - /// Gets a reference to selector's [`Parameters`]. - pub fn parameters(&self) -> &Parameters<'a> { - &self.parameters - } - - /// Gets a mutable reference to selector's [`Parameters`]. - pub fn parameters_mut(&mut self) -> &mut Parameters<'a> { - &mut self.parameters - } - - /// Sets the parameters of this selector. This operation completly overwrites existing [`Parameters`]. - #[inline(always)] - pub fn set_parameters

(&mut self, parameters: P) - where - P: Into>, - { - self.parameters = parameters.into(); - } - - /// Create an owned version of this selector with `'static` lifetime. - pub fn into_owned(self) -> Selector<'static> { - Selector { - key_expr: self.key_expr.into_owned(), - parameters: self.parameters.into_owned(), + /// Build a new selector holding references to keyexpr and parameters + /// Useful for printing pair of keyexpr and parameters in url-like format + pub fn borrowed(key_expr: &'a KeyExpr<'a>, parameters: &'a Parameters<'a>) -> Self { + Self { + key_expr: Cow::Borrowed(key_expr), + parameters: Cow::Borrowed(parameters), } } - - /// Returns this selectors components as a tuple. - pub fn split(self) -> (KeyExpr<'a>, Parameters<'a>) { - (self.key_expr, self.parameters) - } -} - -/// A wrapper type to help decode zenoh selector parameters. -/// -/// Most methods will return an Error if duplicates of a same parameter are found, to avoid HTTP Parameter Pollution like vulnerabilities. -#[repr(transparent)] -#[derive(Clone, PartialEq, Eq)] -pub struct Parameters<'a>(Properties<'a>); - -impl<'a> Deref for Parameters<'a> { - type Target = Properties<'a>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a> DerefMut for Parameters<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl std::fmt::Display for Parameters<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::Debug for Parameters<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl<'a, T> From for Parameters<'a> -where - T: Into>, -{ - fn from(value: T) -> Self { - Parameters(value.into()) - } } -impl<'s> From<&'s Parameters<'s>> for HashMap<&'s str, &'s str> { - fn from(props: &'s Parameters<'s>) -> Self { - HashMap::from(&props.0) +impl<'a> From> for (KeyExpr<'a>, Parameters<'a>) { + fn from(selector: Selector<'a>) -> Self { + ( + selector.key_expr.into_owned(), + selector.parameters.into_owned(), + ) } } -impl From<&Parameters<'_>> for HashMap { - fn from(props: &Parameters) -> Self { - HashMap::from(&props.0) +impl<'a> From<&'a Selector<'a>> for (&'a KeyExpr<'a>, &'a Parameters<'a>) { + fn from(selector: &'a Selector<'a>) -> Self { + (selector.key_expr.as_ref(), selector.parameters.as_ref()) } } -impl From> for HashMap { - fn from(props: Parameters) -> Self { - HashMap::from(props.0) - } +#[zenoh_macros::unstable] +pub trait PredefinedParameters { + const TIME_RANGE_KEY: &'static str = "_time"; + /// Sets the time range targeted by the selector parameters. + fn set_time_range>>(&mut self, time_range: T); + /// Extracts the standardized `_time` argument from the selector parameters. + fn time_range(&self) -> ZResult>; } -impl Parameters<'_> { - /// Create an owned version of these parameters with `'static` lifetime. - pub fn into_owned(self) -> Parameters<'static> { - Parameters(self.0.into_owned()) - } - - #[zenoh_macros::unstable] - /// Sets the time range targeted by the selector. - pub fn set_time_range>>(&mut self, time_range: T) { +#[zenoh_macros::unstable] +impl PredefinedParameters for Parameters<'_> { + /// Sets the time range targeted by the selector parameters. + fn set_time_range>>(&mut self, time_range: T) { let mut time_range: Option = time_range.into(); match time_range.take() { - Some(tr) => self.0.insert(TIME_RANGE_KEY, format!("{}", tr)), - None => self.0.remove(TIME_RANGE_KEY), + Some(tr) => self.insert(TIME_RANGE_KEY, format!("{}", tr)), + None => self.remove(TIME_RANGE_KEY), }; } - #[zenoh_macros::unstable] /// Extracts the standardized `_time` argument from the selector parameters. /// /// The default implementation still causes a complete pass through the selector parameters to ensure that there are no duplicates of the `_time` key. - pub fn time_range(&self) -> ZResult> { - match self.0.get(TIME_RANGE_KEY) { + fn time_range(&self) -> ZResult> { + match self.get(TIME_RANGE_KEY) { Some(tr) => Ok(Some(tr.parse()?)), None => Ok(None), } @@ -243,7 +168,7 @@ impl TryFrom for Selector<'_> { Some(qmark_position) => { let parameters = s[qmark_position + 1..].to_owned(); s.truncate(qmark_position); - Ok(Selector::new(KeyExpr::try_from(s)?, parameters)) + Ok(Selector::owned(KeyExpr::try_from(s)?, parameters)) } None => Ok(KeyExpr::try_from(s)?.into()), } @@ -256,7 +181,7 @@ impl<'a> TryFrom<&'a str> for Selector<'a> { match s.find('?') { Some(qmark_position) => { let params = &s[qmark_position + 1..]; - Ok(Selector::new( + Ok(Selector::owned( KeyExpr::try_from(&s[..qmark_position])?, params, )) @@ -281,18 +206,18 @@ impl<'a> TryFrom<&'a String> for Selector<'a> { impl<'a> From<&'a Query> for Selector<'a> { fn from(q: &'a Query) -> Self { - Selector { - key_expr: q.inner.key_expr.clone(), - parameters: q.inner.parameters.clone(), + Self { + key_expr: Cow::Borrowed(&q.inner.key_expr), + parameters: Cow::Borrowed(&q.inner.parameters), } } } -impl<'a> From<&KeyExpr<'a>> for Selector<'a> { - fn from(key_selector: &KeyExpr<'a>) -> Self { +impl<'a> From<&'a KeyExpr<'a>> for Selector<'a> { + fn from(key_selector: &'a KeyExpr<'a>) -> Self { Self { - key_expr: key_selector.clone(), - parameters: "".into(), + key_expr: Cow::Borrowed(key_selector), + parameters: Cow::Owned("".into()), } } } @@ -300,8 +225,8 @@ impl<'a> From<&KeyExpr<'a>> for Selector<'a> { impl<'a> From<&'a keyexpr> for Selector<'a> { fn from(key_selector: &'a keyexpr) -> Self { Self { - key_expr: key_selector.into(), - parameters: "".into(), + key_expr: Cow::Owned(key_selector.into()), + parameters: Cow::Owned("".into()), } } } @@ -309,8 +234,8 @@ impl<'a> From<&'a keyexpr> for Selector<'a> { impl<'a> From<&'a OwnedKeyExpr> for Selector<'a> { fn from(key_selector: &'a OwnedKeyExpr) -> Self { Self { - key_expr: key_selector.into(), - parameters: "".into(), + key_expr: Cow::Owned(key_selector.into()), + parameters: Cow::Owned("".into()), } } } @@ -318,8 +243,8 @@ impl<'a> From<&'a OwnedKeyExpr> for Selector<'a> { impl From for Selector<'static> { fn from(key_selector: OwnedKeyExpr) -> Self { Self { - key_expr: key_selector.into(), - parameters: "".into(), + key_expr: Cow::Owned(key_selector.into()), + parameters: Cow::Owned("".into()), } } } @@ -327,8 +252,8 @@ impl From for Selector<'static> { impl<'a> From> for Selector<'a> { fn from(key_selector: KeyExpr<'a>) -> Self { Self { - key_expr: key_selector, - parameters: "".into(), + key_expr: Cow::Owned(key_selector), + parameters: Cow::Owned("".into()), } } } @@ -336,63 +261,64 @@ impl<'a> From> for Selector<'a> { #[test] fn selector_accessors() { use crate::api::query::_REPLY_KEY_EXPR_ANY_SEL_PARAM as ANYKE; + use std::collections::HashMap; - for selector in [ + for s in [ "hello/there?_timetrick", "hello/there?_timetrick;_time", "hello/there?_timetrick;_time;_filter", "hello/there?_timetrick;_time=[..]", "hello/there?_timetrick;_time=[..];_filter", ] { - let mut selector = Selector::try_from(selector).unwrap(); - println!("Parameters start: {}", selector.parameters()); - for i in selector.parameters().iter() { + let Selector { + key_expr, + parameters, + } = s.try_into().unwrap(); + assert_eq!(key_expr.as_str(), "hello/there"); + let mut parameters = parameters.into_owned(); + + println!("Parameters start: {}", parameters); + for i in parameters.iter() { println!("\t{:?}", i); } - assert_eq!(selector.parameters().get("_timetrick").unwrap(), ""); + assert_eq!(parameters.get("_timetrick").unwrap(), ""); let time_range = "[now(-2s)..now(2s)]"; zcondfeat!( "unstable", { let time_range = time_range.parse().unwrap(); - selector.parameters_mut().set_time_range(time_range); - assert_eq!( - selector.parameters().time_range().unwrap().unwrap(), - time_range - ); + parameters.set_time_range(time_range); + assert_eq!(parameters.time_range().unwrap().unwrap(), time_range); }, { - selector.parameters_mut().insert(TIME_RANGE_KEY, time_range); + parameters.insert(TIME_RANGE_KEY, time_range); } ); - assert_eq!( - selector.parameters().get(TIME_RANGE_KEY).unwrap(), - time_range - ); + assert_eq!(parameters.get(TIME_RANGE_KEY).unwrap(), time_range); - let hm: HashMap<&str, &str> = HashMap::from(selector.parameters()); + let hm: HashMap<&str, &str> = HashMap::from(¶meters); assert!(hm.contains_key(TIME_RANGE_KEY)); - selector.parameters_mut().insert("_filter", ""); - assert_eq!(selector.parameters().get("_filter").unwrap(), ""); + parameters.insert("_filter", ""); + assert_eq!(parameters.get("_filter").unwrap(), ""); - let hm: HashMap = HashMap::from(selector.parameters()); + let hm: HashMap = HashMap::from(¶meters); assert!(hm.contains_key(TIME_RANGE_KEY)); - selector.parameters_mut().extend_from_iter(hm.iter()); - assert_eq!(selector.parameters().get("_filter").unwrap(), ""); + parameters.extend_from_iter(hm.iter()); + assert_eq!(parameters.get("_filter").unwrap(), ""); - selector.parameters_mut().insert(ANYKE, ""); + parameters.insert(ANYKE, ""); - println!("Parameters end: {}", selector.parameters()); - for i in selector.parameters().iter() { + println!("Parameters end: {}", parameters); + for i in parameters.iter() { println!("\t{:?}", i); } assert_eq!( - HashMap::::from(selector.parameters()), + HashMap::::from(¶meters), HashMap::::from(Parameters::from( "_anyke;_filter;_time=[now(-2s)..now(2s)];_timetrick" )) diff --git a/zenoh/src/api/session.rs b/zenoh/src/api/session.rs index 187ec27be7..ee30e808a7 100644 --- a/zenoh/src/api/session.rs +++ b/zenoh/src/api/session.rs @@ -35,7 +35,8 @@ use zenoh_protocol::network::{declare::SubscriberId, ext}; use zenoh_protocol::{ core::{ key_expr::{keyexpr, OwnedKeyExpr}, - AtomicExprId, CongestionControl, EntityId, ExprId, Reliability, WireExpr, EMPTY_EXPR_ID, + AtomicExprId, CongestionControl, EntityId, ExprId, Parameters, Reliability, WireExpr, + EMPTY_EXPR_ID, }, network::{ self, @@ -1656,7 +1657,8 @@ impl Session { #[allow(clippy::too_many_arguments)] pub(crate) fn query( &self, - selector: &Selector<'_>, + key_expr: &KeyExpr<'_>, + parameters: &Parameters<'_>, scope: &Option>, target: QueryTarget, consolidation: QueryConsolidation, @@ -1668,11 +1670,16 @@ impl Session { #[cfg(feature = "unstable")] source: SourceInfo, callback: Callback<'static, Reply>, ) -> ZResult<()> { - tracing::trace!("get({}, {:?}, {:?})", selector, target, consolidation); + tracing::trace!( + "get({}, {:?}, {:?})", + Selector::borrowed(key_expr, parameters), + target, + consolidation + ); let mut state = zwrite!(self.state); let consolidation = match consolidation.mode { ConsolidationMode::Auto => { - if selector.parameters().contains_key(TIME_RANGE_KEY) { + if parameters.contains_key(TIME_RANGE_KEY) { ConsolidationMode::None } else { ConsolidationMode::Latest @@ -1714,21 +1721,19 @@ impl Session { } }); - let selector = match scope { - Some(scope) => Selector { - key_expr: scope / &*selector.key_expr, - parameters: selector.parameters.clone(), - }, - None => selector.clone(), + let key_expr = match scope { + Some(scope) => scope / key_expr, + None => key_expr.clone().into_owned(), }; tracing::trace!("Register query {} (nb_final = {})", qid, nb_final); - let wexpr = selector.key_expr.to_wire(self).to_owned(); + let wexpr = key_expr.to_wire(self).to_owned(); state.queries.insert( qid, QueryState { nb_final, - selector: selector.clone().into_owned(), + key_expr, + parameters: parameters.clone().into_owned(), scope: scope.clone().map(|e| e.into_owned()), reception_mode: consolidation, replies: (consolidation != ConsolidationMode::None).then(HashMap::new), @@ -1759,7 +1764,7 @@ impl Session { ext_timeout: Some(timeout), payload: RequestBody::Query(zenoh_protocol::zenoh::Query { consolidation, - parameters: selector.parameters().to_string(), + parameters: parameters.to_string(), #[cfg(feature = "unstable")] ext_sinfo: source.into(), #[cfg(not(feature = "unstable"))] @@ -1779,7 +1784,7 @@ impl Session { self.handle_query( true, &wexpr, - selector.parameters().as_str(), + parameters.as_str(), qid, target, consolidation, @@ -2247,18 +2252,15 @@ impl Primitives for Session { Some(query) => { let c = zcondfeat!( "unstable", - !query - .selector - .parameters() - .contains_key(_REPLY_KEY_EXPR_ANY_SEL_PARAM), + !query.parameters.contains_key(_REPLY_KEY_EXPR_ANY_SEL_PARAM), true ); - if c && !query.selector.key_expr.intersects(&key_expr) { + if c && !query.key_expr.intersects(&key_expr) { tracing::warn!( "Received Reply for `{}` from `{:?}, which didn't match query `{}`: dropping Reply.", key_expr, msg.ext_respid, - query.selector + query.selector() ); return; } diff --git a/zenoh/src/lib.rs b/zenoh/src/lib.rs index 0c47070609..3bf3e9f9f7 100644 --- a/zenoh/src/lib.rs +++ b/zenoh/src/lib.rs @@ -247,13 +247,15 @@ pub mod bytes { /// [Selector](https://github.com/eclipse-zenoh/roadmap/tree/main/rfcs/ALL/Selectors) to issue queries pub mod selector { - pub use zenoh_protocol::core::Properties; + #[zenoh_macros::unstable] + pub use crate::api::selector::PredefinedParameters; + pub use zenoh_protocol::core::Parameters; #[zenoh_macros::unstable] pub use zenoh_util::time_range::{TimeBound, TimeExpr, TimeRange}; + pub use crate::api::selector::Selector; #[zenoh_macros::unstable] pub use crate::api::selector::TIME_RANGE_KEY; - pub use crate::api::selector::{Parameters, Selector}; } /// Subscribing primitives diff --git a/zenoh/src/net/runtime/adminspace.rs b/zenoh/src/net/runtime/adminspace.rs index 6b8ac52240..c2505b1f6d 100644 --- a/zenoh/src/net/runtime/adminspace.rs +++ b/zenoh/src/net/runtime/adminspace.rs @@ -787,7 +787,7 @@ fn plugins_data(context: &AdminContext, query: Query) { fn plugins_status(context: &AdminContext, query: Query) { use crate::bytes::{Serialize, ZSerde}; - let selector = query.selector(); + let key_expr = query.key_expr(); let guard = context.runtime.plugins_manager(); let mut root_key = format!( "@/{}/{}/status/plugins/", @@ -820,7 +820,7 @@ fn plugins_status(context: &AdminContext, query: Query) { return; } match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - plugin.instance().adminspace_getter(&selector, plugin_key) + plugin.instance().adminspace_getter(&key_expr, plugin_key) })) { Ok(Ok(responses)) => { for response in responses {