use std::{borrow::Cow, convert::TryFrom, str::FromStr};
use zenoh_protocol::core::{
key_expr::{keyexpr, OwnedKeyExpr},
Parameters,
};
#[cfg(feature = "unstable")]
use ::{zenoh_result::ZResult, zenoh_util::time_range::TimeRange};
use crate::api::{key_expr::KeyExpr, queryable::Query};
#[derive(Clone, PartialEq, Eq)]
pub struct Selector<'a> {
pub(crate) key_expr: Cow<'a, KeyExpr<'a>>,
pub(crate) parameters: Cow<'a, Parameters<'a>>,
}
impl<'a> Selector<'a> {
pub fn key_expr(&self) -> &KeyExpr<'a> {
&self.key_expr
}
pub fn parameters(&self) -> &Parameters<'a> {
&self.parameters
}
pub fn split(self) -> (KeyExpr<'a>, Parameters<'a>) {
self.into()
}
pub fn owned<K, P>(key_expr: K, parameters: P) -> Self
where
K: Into<KeyExpr<'a>>,
P: Into<Parameters<'a>>,
{
Self {
key_expr: Cow::Owned(key_expr.into()),
parameters: Cow::Owned(parameters.into()),
}
}
pub fn borrowed(key_expr: &'a KeyExpr<'a>, parameters: &'a Parameters<'a>) -> Self {
Self {
key_expr: Cow::Borrowed(key_expr),
parameters: Cow::Borrowed(parameters),
}
}
pub fn into_owned(self) -> Selector<'static> {
Selector::owned(
self.key_expr.into_owned().into_owned(),
self.parameters.into_owned().into_owned(),
)
}
}
impl<'a, K, P> From<(K, P)> for Selector<'a>
where
K: Into<KeyExpr<'a>>,
P: Into<Parameters<'a>>,
{
fn from((key_expr, parameters): (K, P)) -> Self {
Self::owned(key_expr, parameters)
}
}
impl<'a> From<Selector<'a>> for (KeyExpr<'a>, Parameters<'a>) {
fn from(selector: Selector<'a>) -> Self {
(
selector.key_expr.into_owned(),
selector.parameters.into_owned(),
)
}
}
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())
}
}
pub(crate) const REPLY_KEY_EXPR_ANY_SEL_PARAM: &str = "_anyke";
#[zenoh_macros::unstable]
pub(crate) const TIME_RANGE_KEY: &str = "_time";
#[zenoh_macros::unstable]
pub trait ZenohParameters {
fn set_time_range<T: Into<Option<TimeRange>>>(&mut self, time_range: T);
fn set_reply_key_expr_any(&mut self);
fn time_range(&self) -> Option<ZResult<TimeRange>>;
fn reply_key_expr_any(&self) -> bool;
}
#[cfg(feature = "unstable")]
impl ZenohParameters for Parameters<'_> {
fn set_time_range<T: Into<Option<TimeRange>>>(&mut self, time_range: T) {
let mut time_range: Option<TimeRange> = time_range.into();
match time_range.take() {
Some(tr) => self.insert(TIME_RANGE_KEY, format!("{tr}")),
None => self.remove(TIME_RANGE_KEY),
};
}
fn set_reply_key_expr_any(&mut self) {
self.insert(REPLY_KEY_EXPR_ANY_SEL_PARAM, "");
}
fn time_range(&self) -> Option<ZResult<TimeRange>> {
self.get(TIME_RANGE_KEY)
.map(|tr| tr.parse().map_err(Into::into))
}
fn reply_key_expr_any(&self) -> bool {
self.contains_key(REPLY_KEY_EXPR_ANY_SEL_PARAM)
}
}
impl std::fmt::Debug for Selector<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "sel\"{self}\"")
}
}
impl std::fmt::Display for Selector<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.key_expr)?;
if !self.parameters.is_empty() {
write!(f, "?{}", self.parameters.as_str())?;
}
Ok(())
}
}
impl<'a> From<&Selector<'a>> for Selector<'a> {
fn from(s: &Selector<'a>) -> Self {
s.clone()
}
}
impl TryFrom<String> for Selector<'_> {
type Error = zenoh_result::Error;
fn try_from(mut s: String) -> Result<Self, Self::Error> {
match s.find('?') {
Some(qmark_position) => {
let parameters = s[qmark_position + 1..].to_owned();
s.truncate(qmark_position);
Ok(Selector::owned(KeyExpr::try_from(s)?, parameters))
}
None => Ok(KeyExpr::try_from(s)?.into()),
}
}
}
impl<'a> TryFrom<&'a str> for Selector<'a> {
type Error = zenoh_result::Error;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
match s.find('?') {
Some(qmark_position) => {
let params = &s[qmark_position + 1..];
Ok(Selector::owned(
KeyExpr::try_from(&s[..qmark_position])?,
params,
))
}
None => Ok(KeyExpr::try_from(s)?.into()),
}
}
}
impl FromStr for Selector<'static> {
type Err = zenoh_result::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.to_owned().try_into()
}
}
impl<'a> TryFrom<&'a String> for Selector<'a> {
type Error = zenoh_result::Error;
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
Self::try_from(s.as_str())
}
}
impl<'a> From<&'a Query> for Selector<'a> {
fn from(q: &'a Query) -> Self {
Self {
key_expr: Cow::Borrowed(&q.inner.key_expr),
parameters: Cow::Borrowed(&q.inner.parameters),
}
}
}
impl<'a> From<&'a KeyExpr<'a>> for Selector<'a> {
fn from(key_selector: &'a KeyExpr<'a>) -> Self {
Self {
key_expr: Cow::Borrowed(key_selector),
parameters: Cow::Owned("".into()),
}
}
}
impl<'a> From<&'a keyexpr> for Selector<'a> {
fn from(key_selector: &'a keyexpr) -> Self {
Self {
key_expr: Cow::Owned(key_selector.into()),
parameters: Cow::Owned("".into()),
}
}
}
impl<'a> From<&'a OwnedKeyExpr> for Selector<'a> {
fn from(key_selector: &'a OwnedKeyExpr) -> Self {
Self {
key_expr: Cow::Owned(key_selector.into()),
parameters: Cow::Owned("".into()),
}
}
}
impl From<OwnedKeyExpr> for Selector<'static> {
fn from(key_selector: OwnedKeyExpr) -> Self {
Self {
key_expr: Cow::Owned(key_selector.into()),
parameters: Cow::Owned("".into()),
}
}
}
impl<'a> From<KeyExpr<'a>> for Selector<'a> {
fn from(key_selector: KeyExpr<'a>) -> Self {
Self {
key_expr: Cow::Owned(key_selector),
parameters: Cow::Owned("".into()),
}
}
}
#[cfg(feature = "unstable")]
#[test]
fn selector_accessors() {
use std::collections::HashMap;
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 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!(parameters.get("_timetrick").unwrap(), "");
const ANYKE: &str = REPLY_KEY_EXPR_ANY_SEL_PARAM;
let time_range = "[now(-2s)..now(2s)]";
zcondfeat!(
"unstable",
{
let time_range = time_range.parse().unwrap();
parameters.set_time_range(time_range);
assert_eq!(parameters.time_range().unwrap().unwrap(), time_range);
},
{
parameters.insert(TIME_RANGE_KEY, time_range);
}
);
assert_eq!(parameters.get(TIME_RANGE_KEY).unwrap(), time_range);
let hm: HashMap<&str, &str> = HashMap::from(¶meters);
assert!(hm.contains_key(TIME_RANGE_KEY));
parameters.insert("_filter", "");
assert_eq!(parameters.get("_filter").unwrap(), "");
let hm: HashMap<String, String> = HashMap::from(¶meters);
assert!(hm.contains_key(TIME_RANGE_KEY));
parameters.extend_from_iter(hm.iter());
assert_eq!(parameters.get("_filter").unwrap(), "");
parameters.insert(ANYKE, "");
println!("Parameters end: {parameters}");
for i in parameters.iter() {
println!("\t{i:?}");
}
assert_eq!(
HashMap::<String, String>::from(¶meters),
HashMap::<String, String>::from(Parameters::from(
"_anyke;_filter;_time=[now(-2s)..now(2s)];_timetrick"
))
);
}
}