Skip to main content

zenoh/api/
selector.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//! [Selector](https://github.com/eclipse-zenoh/roadmap/tree/main/rfcs/ALL/Selectors) to issue queries
16use std::{borrow::Cow, convert::TryFrom, str::FromStr};
17
18use zenoh_protocol::core::{
19    key_expr::{keyexpr, OwnedKeyExpr},
20    Parameters,
21};
22#[cfg(feature = "unstable")]
23use ::{zenoh_result::ZResult, zenoh_util::time_range::TimeRange};
24
25use crate::api::{key_expr::KeyExpr, queryable::Query};
26
27/// A selector is the combination of a [`Key Expression`](crate::key_expr::KeyExpr), which defines the
28/// set of keys that are relevant to an operation, and a set of [`Parameters`](crate::query::Parameters),
29/// with a few intended uses:
30/// - specifying arguments to a queryable, allowing the passing of Remote Procedure Call parameters,
31/// - filtering by value,
32/// - filtering by metadata, such as the timestamp of a value,
33/// - specifying arguments to zenoh when using the [REST API](https://zenoh.io/docs/apis/rest/).
34///
35/// When in string form, selectors look a lot like a URI, with similar semantics:
36/// - the `key_expr` before the first `?` must be a valid key expression.
37/// - the `parameters` after the first `?` should be encoded like the query section of a URL:
38///     - parameters are separated by `;`,
39///     - the parameter name and value are separated by the first `=`,
40///     - in the absence of `=`, the parameter value is considered to be the empty string,
41///     - both name and value should use percent-encoding to escape characters,
42///     - defining a value for the same parameter name twice is considered undefined behavior,
43///       with the encouraged behavior being to reject operations when a duplicate parameter is detected.
44///
45/// Zenoh intends to standardize the usage of a set of parameter names. To avoid conflicting with RPC parameters,
46/// the Zenoh team has settled on reserving the set of parameter names that start with non-alphanumeric characters.
47///
48/// [Queryable](crate::query::Queryable) implementers are encouraged to prefer these standardized parameter names when implementing their
49/// associated features, and to prefix their own parameter names to avoid having conflicting parameter names with other
50/// queryables.
51///
52/// Here are the currently standardized parameters for Zenoh (check the
53/// [specification page](https://github.com/eclipse-zenoh/roadmap/tree/main/rfcs/ALL/Selectors)
54/// for the exhaustive list):
55/// - **`[unstable]`** `_time`: used to express interest in only values dated within a certain time range; values for
56///   this parameter must be readable by the [Zenoh Time DSL](zenoh_util::time_range::TimeRange) for the value to be considered valid.
57/// - **`[unstable]`** `_anyke`: used in queries to express interest in replies coming from any key expression. By default, only replies
58///   whose key expression matches the query's key expression are accepted. `_anyke` disables the query-reply key expression matching check.
59///   This parameter is set by the `accept_replies` method on query builders, such as [`SessionGetBuilder::accept_replies`](crate::session::SessionGetBuilder::accept_replies)
60///   and [`QuerierBuilder::accept_replies`](crate::query::QuerierBuilder::accept_replies).
61#[derive(Clone, PartialEq, Eq)]
62pub struct Selector<'a> {
63    /// The part of this selector identifying which keys should be part of the selection.
64    pub(crate) key_expr: Cow<'a, KeyExpr<'a>>,
65    /// The part of this selector identifying which values should be part of the selection.
66    pub(crate) parameters: Cow<'a, Parameters<'a>>,
67}
68
69impl<'a> Selector<'a> {
70    /// Get the [`KeyExpr`] of this selector.
71    pub fn key_expr(&self) -> &KeyExpr<'a> {
72        &self.key_expr
73    }
74
75    /// Get the [`Parameters`] of this selector.
76    pub fn parameters(&self) -> &Parameters<'a> {
77        &self.parameters
78    }
79
80    /// Deconstruct the selector into ([`KeyExpr`], [`Parameters`])
81    pub fn split(self) -> (KeyExpr<'a>, Parameters<'a>) {
82        self.into()
83    }
84
85    /// Builds a new selector which owns keyexpr and parameters
86    pub fn owned<K, P>(key_expr: K, parameters: P) -> Self
87    where
88        K: Into<KeyExpr<'a>>,
89        P: Into<Parameters<'a>>,
90    {
91        Self {
92            key_expr: Cow::Owned(key_expr.into()),
93            parameters: Cow::Owned(parameters.into()),
94        }
95    }
96    /// Build a new selector holding references to keyexpr and parameters
97    /// Useful for printing pairs of keyexpr and parameters in URL-like format
98    pub fn borrowed(key_expr: &'a KeyExpr<'a>, parameters: &'a Parameters<'a>) -> Self {
99        Self {
100            key_expr: Cow::Borrowed(key_expr),
101            parameters: Cow::Borrowed(parameters),
102        }
103    }
104
105    /// Convert this selector into an owned one.
106    pub fn into_owned(self) -> Selector<'static> {
107        Selector::owned(
108            self.key_expr.into_owned().into_owned(),
109            self.parameters.into_owned().into_owned(),
110        )
111    }
112}
113
114impl<'a, K, P> From<(K, P)> for Selector<'a>
115where
116    K: Into<KeyExpr<'a>>,
117    P: Into<Parameters<'a>>,
118{
119    fn from((key_expr, parameters): (K, P)) -> Self {
120        Self::owned(key_expr, parameters)
121    }
122}
123
124impl<'a> From<Selector<'a>> for (KeyExpr<'a>, Parameters<'a>) {
125    fn from(selector: Selector<'a>) -> Self {
126        (
127            selector.key_expr.into_owned(),
128            selector.parameters.into_owned(),
129        )
130    }
131}
132
133impl<'a> From<&'a Selector<'a>> for (&'a KeyExpr<'a>, &'a Parameters<'a>) {
134    fn from(selector: &'a Selector<'a>) -> Self {
135        (selector.key_expr.as_ref(), selector.parameters.as_ref())
136    }
137}
138
139pub(crate) const REPLY_KEY_EXPR_ANY_SEL_PARAM: &str = "_anyke";
140#[zenoh_macros::unstable]
141pub(crate) const TIME_RANGE_KEY: &str = "_time";
142
143#[zenoh_macros::unstable]
144/// The trait allows setting/reading parameters processed by the Zenoh library itself.
145pub trait ZenohParameters {
146    /// These parameter names are not part of the public API. They are exposed only to provide information about current parameter
147    /// names, allowing users to avoid conflicts with custom parameters. It's also possible that some of these Zenoh-specific parameters,
148    /// which are now stored as key-value pairs, will later be passed in some other way, while keeping the same get/set interface functions.
149    /// Sets the time range targeted by the selector parameters.
150    fn set_time_range<T: Into<Option<TimeRange>>>(&mut self, time_range: T);
151    /// Sets the parameter allowing replies from queryables not matching
152    /// the requested key expression. This may happen in this scenario:
153    /// - we are requesting keyexpr `a/b`.
154    /// - queryable is declared to handle `a/*` queries and contains data for `a/b` and `a/c`.
155    /// - queryable receives our request and sends two replies with data for `a/b` and `a/c`
156    ///
157    /// Normally, only the `a/b` reply would be accepted, but with the `_anyke` parameter set, both replies are accepted.
158    /// NOTE: `_anyke` indicates that ANY key expression is allowed. i.e., if the `_anyke` parameter is set, a reply
159    ///       on `x/y/z` is valid even if the queryable is declared on `a/*`.
160    fn set_reply_key_expr_any(&mut self);
161    /// Extracts the standardized `_time` argument from the selector parameters.
162    /// Returns `None` if the `_time` argument is not present or `Some` with the result of parsing the `_time` argument
163    /// if it is present.
164    fn time_range(&self) -> Option<ZResult<TimeRange>>;
165    /// Returns true if the `_anyke` parameter is present in the selector parameters
166    fn reply_key_expr_any(&self) -> bool;
167}
168
169#[cfg(feature = "unstable")]
170impl ZenohParameters for Parameters<'_> {
171    /// Sets the time range targeted by the selector parameters.
172    fn set_time_range<T: Into<Option<TimeRange>>>(&mut self, time_range: T) {
173        let mut time_range: Option<TimeRange> = time_range.into();
174        match time_range.take() {
175            Some(tr) => self.insert(TIME_RANGE_KEY, format!("{tr}")),
176            None => self.remove(TIME_RANGE_KEY),
177        };
178    }
179
180    /// Sets the parameter allowing the querier to reply to this request even
181    /// if the requested key expression does not match the reply key expression.
182    fn set_reply_key_expr_any(&mut self) {
183        self.insert(REPLY_KEY_EXPR_ANY_SEL_PARAM, "");
184    }
185
186    /// Extracts the standardized `_time` argument from the selector parameters.
187    ///
188    /// The default implementation still causes a complete pass through the selector parameters to ensure that there are no duplicates of the `_time` key.
189    fn time_range(&self) -> Option<ZResult<TimeRange>> {
190        self.get(TIME_RANGE_KEY)
191            .map(|tr| tr.parse().map_err(Into::into))
192    }
193
194    /// Returns true if the `_anyke` parameter is present in the selector parameters
195    fn reply_key_expr_any(&self) -> bool {
196        self.contains_key(REPLY_KEY_EXPR_ANY_SEL_PARAM)
197    }
198}
199
200impl std::fmt::Debug for Selector<'_> {
201    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
202        write!(f, "sel\"{self}\"")
203    }
204}
205
206impl std::fmt::Display for Selector<'_> {
207    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
208        write!(f, "{}", self.key_expr)?;
209        if !self.parameters.is_empty() {
210            write!(f, "?{}", self.parameters.as_str())?;
211        }
212        Ok(())
213    }
214}
215
216impl<'a> From<&Selector<'a>> for Selector<'a> {
217    fn from(s: &Selector<'a>) -> Self {
218        s.clone()
219    }
220}
221
222impl TryFrom<String> for Selector<'_> {
223    type Error = zenoh_result::Error;
224    fn try_from(mut s: String) -> Result<Self, Self::Error> {
225        match s.find('?') {
226            Some(qmark_position) => {
227                let parameters = s[qmark_position + 1..].to_owned();
228                s.truncate(qmark_position);
229                Ok(Selector::owned(KeyExpr::try_from(s)?, parameters))
230            }
231            None => Ok(KeyExpr::try_from(s)?.into()),
232        }
233    }
234}
235
236impl<'a> TryFrom<&'a str> for Selector<'a> {
237    type Error = zenoh_result::Error;
238    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
239        match s.find('?') {
240            Some(qmark_position) => {
241                let params = &s[qmark_position + 1..];
242                Ok(Selector::owned(
243                    KeyExpr::try_from(&s[..qmark_position])?,
244                    params,
245                ))
246            }
247            None => Ok(KeyExpr::try_from(s)?.into()),
248        }
249    }
250}
251impl FromStr for Selector<'static> {
252    type Err = zenoh_result::Error;
253    fn from_str(s: &str) -> Result<Self, Self::Err> {
254        s.to_owned().try_into()
255    }
256}
257
258impl<'a> TryFrom<&'a String> for Selector<'a> {
259    type Error = zenoh_result::Error;
260    fn try_from(s: &'a String) -> Result<Self, Self::Error> {
261        Self::try_from(s.as_str())
262    }
263}
264
265impl<'a> From<&'a Query> for Selector<'a> {
266    fn from(q: &'a Query) -> Self {
267        Self {
268            key_expr: Cow::Borrowed(&q.inner.key_expr),
269            parameters: Cow::Borrowed(&q.inner.parameters),
270        }
271    }
272}
273
274impl<'a> From<&'a KeyExpr<'a>> for Selector<'a> {
275    fn from(key_selector: &'a KeyExpr<'a>) -> Self {
276        Self {
277            key_expr: Cow::Borrowed(key_selector),
278            parameters: Cow::Owned("".into()),
279        }
280    }
281}
282
283impl<'a> From<&'a keyexpr> for Selector<'a> {
284    fn from(key_selector: &'a keyexpr) -> Self {
285        Self {
286            key_expr: Cow::Owned(key_selector.into()),
287            parameters: Cow::Owned("".into()),
288        }
289    }
290}
291
292impl<'a> From<&'a OwnedKeyExpr> for Selector<'a> {
293    fn from(key_selector: &'a OwnedKeyExpr) -> Self {
294        Self {
295            key_expr: Cow::Owned(key_selector.into()),
296            parameters: Cow::Owned("".into()),
297        }
298    }
299}
300
301impl From<OwnedKeyExpr> for Selector<'static> {
302    fn from(key_selector: OwnedKeyExpr) -> Self {
303        Self {
304            key_expr: Cow::Owned(key_selector.into()),
305            parameters: Cow::Owned("".into()),
306        }
307    }
308}
309
310impl<'a> From<KeyExpr<'a>> for Selector<'a> {
311    fn from(key_selector: KeyExpr<'a>) -> Self {
312        Self {
313            key_expr: Cow::Owned(key_selector),
314            parameters: Cow::Owned("".into()),
315        }
316    }
317}
318
319#[cfg(feature = "unstable")]
320#[test]
321fn selector_accessors() {
322    use std::collections::HashMap;
323
324    for s in [
325        "hello/there?_timetrick",
326        "hello/there?_timetrick;_time",
327        "hello/there?_timetrick;_time;_filter",
328        "hello/there?_timetrick;_time=[..]",
329        "hello/there?_timetrick;_time=[..];_filter",
330    ] {
331        let Selector {
332            key_expr,
333            parameters,
334        } = s.try_into().unwrap();
335        assert_eq!(key_expr.as_str(), "hello/there");
336        let mut parameters = parameters.into_owned();
337
338        println!("Parameters start: {parameters}");
339        for i in parameters.iter() {
340            println!("\t{i:?}");
341        }
342
343        assert_eq!(parameters.get("_timetrick").unwrap(), "");
344
345        const ANYKE: &str = REPLY_KEY_EXPR_ANY_SEL_PARAM;
346
347        let time_range = "[now(-2s)..now(2s)]";
348        zcondfeat!(
349            "unstable",
350            {
351                let time_range = time_range.parse().unwrap();
352                parameters.set_time_range(time_range);
353                assert_eq!(parameters.time_range().unwrap().unwrap(), time_range);
354            },
355            {
356                parameters.insert(TIME_RANGE_KEY, time_range);
357            }
358        );
359        assert_eq!(parameters.get(TIME_RANGE_KEY).unwrap(), time_range);
360
361        let hm: HashMap<&str, &str> = HashMap::from(&parameters);
362        assert!(hm.contains_key(TIME_RANGE_KEY));
363
364        parameters.insert("_filter", "");
365        assert_eq!(parameters.get("_filter").unwrap(), "");
366
367        let hm: HashMap<String, String> = HashMap::from(&parameters);
368        assert!(hm.contains_key(TIME_RANGE_KEY));
369
370        parameters.extend_from_iter(hm.iter());
371        assert_eq!(parameters.get("_filter").unwrap(), "");
372
373        parameters.insert(ANYKE, "");
374
375        println!("Parameters end: {parameters}");
376        for i in parameters.iter() {
377            println!("\t{i:?}");
378        }
379
380        assert_eq!(
381            HashMap::<String, String>::from(&parameters),
382            HashMap::<String, String>::from(Parameters::from(
383                "_anyke;_filter;_time=[now(-2s)..now(2s)];_timetrick"
384            ))
385        );
386    }
387}