diff --git a/commons/zenoh-protocol/src/core/mod.rs b/commons/zenoh-protocol/src/core/mod.rs index 2547034c44..bff70b3d6e 100644 --- a/commons/zenoh-protocol/src/core/mod.rs +++ b/commons/zenoh-protocol/src/core/mod.rs @@ -61,6 +61,7 @@ pub struct Property { } /// The kind of a `Sample`. +// tags{publisher.write.kind} #[repr(u8)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub enum SampleKind { @@ -92,6 +93,7 @@ impl TryFrom for SampleKind { } /// The global unique id of a zenoh peer. +// tags{zenoh_id} #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct ZenohId(uhlc::ID); @@ -345,6 +347,8 @@ impl TryFrom for Priority { } } +/// The reliability request of a subscriber. +// tags{enum.reliability} #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum Reliability { @@ -422,6 +426,7 @@ pub enum ConsolidationMode { } /// The `zenoh::queryable::Queryable`s that should be target of a `zenoh::Session::get()`. +// tags{enum.query.target} #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum QueryTarget { #[default] diff --git a/commons/zenoh-protocol/src/network/mod.rs b/commons/zenoh-protocol/src/network/mod.rs index 1be58db5cc..af7ee6d5f5 100644 --- a/commons/zenoh-protocol/src/network/mod.rs +++ b/commons/zenoh-protocol/src/network/mod.rs @@ -11,6 +11,8 @@ // Contributors: // ZettaScale Zenoh Team, // + +// ignore_tagging pub mod declare; pub mod oam; pub mod push; diff --git a/commons/zenoh-protocol/src/scouting/mod.rs b/commons/zenoh-protocol/src/scouting/mod.rs index 9e7fd27c2d..3f288e79b3 100644 --- a/commons/zenoh-protocol/src/scouting/mod.rs +++ b/commons/zenoh-protocol/src/scouting/mod.rs @@ -11,6 +11,8 @@ // Contributors: // ZettaScale Zenoh Team, // + +// ignore_tagging pub mod hello; pub mod scout; diff --git a/commons/zenoh-protocol/src/transport/mod.rs b/commons/zenoh-protocol/src/transport/mod.rs index cdf994e5dd..ef531459ca 100644 --- a/commons/zenoh-protocol/src/transport/mod.rs +++ b/commons/zenoh-protocol/src/transport/mod.rs @@ -11,6 +11,8 @@ // Contributors: // ZettaScale Zenoh Team, // + +// ignore_tagging pub mod close; pub mod fragment; pub mod frame; diff --git a/commons/zenoh-protocol/src/zenoh/mod.rs b/commons/zenoh-protocol/src/zenoh/mod.rs index e67576e673..6bb6bc96c6 100644 --- a/commons/zenoh-protocol/src/zenoh/mod.rs +++ b/commons/zenoh-protocol/src/zenoh/mod.rs @@ -11,6 +11,8 @@ // Contributors: // ZettaScale Zenoh Team, // + +// ignore_tagging pub mod ack; pub mod del; pub mod err; diff --git a/zenoh/src/handlers.rs b/zenoh/src/handlers.rs index 69828a5d7f..59e9a007db 100644 --- a/zenoh/src/handlers.rs +++ b/zenoh/src/handlers.rs @@ -26,6 +26,7 @@ pub type Callback<'a, T> = Dyn; /// while granting you access to the receiver through the returned value via [`std::ops::Deref`] and [`std::ops::DerefMut`]. /// /// Any closure that accepts `T` can be converted into a pair of itself and `()`. +// ignore_tagging pub trait IntoCallbackReceiverPair<'a, T> { type Receiver; fn into_cb_receiver_pair(self) -> (Callback<'a, T>, Self::Receiver); @@ -56,6 +57,8 @@ impl IntoCallbackReceiverPair<'static, T> ) } } + +// ignore_tagging pub struct DefaultHandler; impl IntoCallbackReceiverPair<'static, T> for DefaultHandler { type Receiver = flume::Receiver; @@ -82,6 +85,7 @@ impl IntoCallbackReceiverPair<'static, T> /// A function that can transform a [`FnMut`]`(T)` to /// a [`Fn`]`(T)` with the help of a [`Mutex`](std::sync::Mutex). +// ignore_tagging pub fn locked(fnmut: impl FnMut(T)) -> impl Fn(T) { let lock = std::sync::Mutex::new(fnmut); move |x| zlock!(lock)(x) @@ -96,6 +100,7 @@ pub fn locked(fnmut: impl FnMut(T)) -> impl Fn(T) { /// - `callback` will never be called once `drop` has started. /// - `drop` will only be called **once**, and **after every** `callback` has ended. /// - The two previous guarantees imply that `call` and `drop` are never called concurrently. +// ignore_tagging pub struct CallbackPair where DropFn: FnMut() + Send + Sync + 'static, diff --git a/zenoh/src/info.rs b/zenoh/src/info.rs index 848a5cb68b..107880e91a 100644 --- a/zenoh/src/info.rs +++ b/zenoh/src/info.rs @@ -31,6 +31,7 @@ use zenoh_protocol::core::{WhatAmI, ZenohId}; /// let zid = session.info().zid().res().await; /// # }) /// ``` +// tags{} #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct ZidBuilder<'a> { @@ -69,6 +70,7 @@ impl<'a> AsyncResolve for ZidBuilder<'a> { /// while let Some(router_zid) = routers_zid.next() {} /// # }) /// ``` +// tags{} #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct RoutersZidBuilder<'a> { @@ -116,6 +118,7 @@ impl<'a> AsyncResolve for RoutersZidBuilder<'a> { /// while let Some(peer_zid) = peers_zid.next() {} /// # }) /// ``` +// tags{} #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct PeersZidBuilder<'a> { @@ -162,6 +165,7 @@ impl<'a> AsyncResolve for PeersZidBuilder<'a> { /// let zid = info.zid().res().await; /// # }) /// ``` +// tags{} pub struct SessionInfo<'a> { pub(crate) session: SessionRef<'a>, } @@ -178,6 +182,7 @@ impl SessionInfo<'_> { /// let zid = session.info().zid().res().await; /// # }) /// ``` + // tags{session.zid.get} pub fn zid(&self) -> ZidBuilder<'_> { ZidBuilder { session: self.session.clone(), @@ -197,6 +202,7 @@ impl SessionInfo<'_> { /// while let Some(router_zid) = routers_zid.next() {} /// # }) /// ``` + // tags{session.routers.get} pub fn routers_zid(&self) -> RoutersZidBuilder<'_> { RoutersZidBuilder { session: self.session.clone(), @@ -215,6 +221,7 @@ impl SessionInfo<'_> { /// while let Some(peer_zid) = peers_zid.next() {} /// # }) /// ``` + // tags{session.peers.get} pub fn peers_zid(&self) -> PeersZidBuilder<'_> { PeersZidBuilder { session: self.session.clone(), diff --git a/zenoh/src/key_expr.rs b/zenoh/src/key_expr.rs index d2295f9798..5c60407baf 100644 --- a/zenoh/src/key_expr.rs +++ b/zenoh/src/key_expr.rs @@ -52,6 +52,8 @@ pub(crate) enum KeyExprInner<'a> { /// A possibly-owned version of [`keyexpr`] that may carry optimisations for use with a [`Session`] that may have declared it. /// /// Check [`keyexpr`]'s documentation for detailed explainations of the Key Expression Language. +/// +// tags{keyexpr} #[repr(transparent)] #[derive(Clone, serde::Deserialize, serde::Serialize)] #[serde(from = "OwnedKeyExpr")] @@ -74,6 +76,7 @@ impl KeyExpr<'static> { /// # Safety /// Key Expressions must follow some rules to be accepted by a Zenoh network. /// Messages addressed with invalid key expressions will be dropped. + // tags{keyexpr.create.from_string_unchecked} pub unsafe fn from_string_unchecked(s: String) -> Self { Self(KeyExprInner::Owned(OwnedKeyExpr::from_string_unchecked(s))) } @@ -82,6 +85,7 @@ impl KeyExpr<'static> { /// # Safety /// Key Expressions must follow some rules to be accepted by a Zenoh network. /// Messages addressed with invalid key expressions will be dropped. + // tags{keyexpr.create.from_string_unchecked} pub unsafe fn from_boxed_string_unchecked(s: Box) -> Self { Self(KeyExprInner::Owned( OwnedKeyExpr::from_boxed_string_unchecked(s), @@ -95,6 +99,7 @@ impl<'a> KeyExpr<'a> { /// Note that to be considered a valid key expression, a string MUST be canon. /// /// [`KeyExpr::autocanonize`] is an alternative constructor that will canonize the passed expression before constructing it. + // tags{keyexpr.create} pub fn new(t: T) -> Result where Self: TryFrom, @@ -105,6 +110,7 @@ impl<'a> KeyExpr<'a> { /// Constructs a new [`KeyExpr`] aliasing `self`. /// /// Note that [`KeyExpr`] (as well as [`OwnedKeyExpr`]) use reference counters internally, so you're probably better off using clone. + // ignore_tagging pub fn borrowing_clone(&'a self) -> Self { let inner = match &self.0 { KeyExprInner::Borrowed(key_expr) => KeyExprInner::Borrowed(key_expr), @@ -142,6 +148,7 @@ impl<'a> KeyExpr<'a> { /// Canonizes the passed value before returning it as a `KeyExpr`. /// /// Will return Err if the passed value isn't a valid key expression despite canonization. + // tags{keyexpr.autocanonize} pub fn autocanonize(mut t: T) -> Result where Self: TryFrom, @@ -155,16 +162,19 @@ impl<'a> KeyExpr<'a> { /// # Safety /// Key Expressions must follow some rules to be accepted by a Zenoh network. /// Messages addressed with invalid key expressions will be dropped. - pub unsafe fn from_str_uncheckend(s: &'a str) -> Self { + // tags{keyexpr.create.from_string_unchecked} + pub unsafe fn from_str_unchecked(s: &'a str) -> Self { keyexpr::from_str_unchecked(s).into() } /// Returns the borrowed version of `self` + // ignore_tagging pub fn as_keyexpr(&self) -> &keyexpr { self } /// Ensures `self` owns all of its data, and informs rustc that it does. + // ignore_tagging pub fn into_owned(self) -> KeyExpr<'static> { match self.0 { KeyExprInner::Borrowed(s) => KeyExpr(KeyExprInner::Owned(s.into())), @@ -210,6 +220,7 @@ impl<'a> KeyExpr<'a> { /// let workspace: KeyExpr = get_workspace(); /// let topic = workspace.join("some/topic").unwrap(); /// ``` + // tags{keyexpr.join} pub fn join + ?Sized>(&self, s: &S) -> ZResult> { let r = self.as_keyexpr().join(s)?; if let KeyExprInner::Wire { @@ -235,6 +246,7 @@ impl<'a> KeyExpr<'a> { /// Performs string concatenation and returns the result as a [`KeyExpr`] if possible. /// /// You should probably prefer [`KeyExpr::join`] as Zenoh may then take advantage of the hierachical separation it inserts. + // tags{keyexpr.concat} pub fn concat + ?Sized>(&self, s: &S) -> ZResult> { let s = s.as_ref(); self._concat(s) @@ -272,6 +284,7 @@ impl<'a> KeyExpr<'a> { } } + // tags{selector.create.from_keyexpr} pub fn with_parameters(self, selector: &'a str) -> Selector<'a> { Selector { key_expr: self, @@ -279,6 +292,7 @@ impl<'a> KeyExpr<'a> { } } + // tags{selector.create.from_keyexpr} pub fn with_owned_parameters(self, selector: String) -> Selector<'a> { Selector { key_expr: self, @@ -584,6 +598,7 @@ impl<'a> Undeclarable<&'a Session, KeyExprUndeclaration<'a>> for KeyExpr<'a> { /// session.undeclare(key_expr).res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct KeyExprUndeclaration<'a> { session: &'a Session, diff --git a/zenoh/src/lib.rs b/zenoh/src/lib.rs index 5c3b938e5b..e6b7938da1 100644 --- a/zenoh/src/lib.rs +++ b/zenoh/src/lib.rs @@ -99,6 +99,7 @@ pub use zenoh_result::ZResult as Result; const GIT_VERSION: &str = git_version!(prefix = "v", cargo_prefix = "v"); +// ignore_tagging pub const FEATURES: &str = concat_enabled_features!( prefix = "zenoh", features = [ @@ -160,6 +161,7 @@ pub mod time { /// Generates a reception [`Timestamp`] with id=0x01. /// This operation should be called if a timestamp is required for an incoming [`zenoh::Sample`](crate::Sample) /// that doesn't contain any timestamp. + // tags{timestamp.create} pub fn new_reception_timestamp() -> Timestamp { use std::time::{SystemTime, UNIX_EPOCH}; @@ -176,6 +178,7 @@ pub mod properties { /// Convert a set of [`Properties`] into a [`Value`]. /// For instance, Properties: `[("k1", "v1"), ("k2, v2")]` /// is converted into Json: `{ "k1": "v1", "k2": "v2" }` + // tags{value.create.from_properties} pub fn properties_to_json_value(props: &Properties) -> Value { let json_map = props .iter() @@ -214,6 +217,7 @@ pub mod scouting; /// } /// # }) /// ``` +// tags{scout} pub fn scout, TryIntoConfig>( what: I, config: TryIntoConfig, @@ -257,6 +261,7 @@ where /// let session = zenoh::open(config).res().await.unwrap(); /// # }) /// ``` +// tags{session.create} pub fn open(config: TryIntoConfig) -> OpenBuilder where TryIntoConfig: std::convert::TryInto + Send + 'static, @@ -275,6 +280,7 @@ where /// let session = zenoh::open(config::peer()).res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct OpenBuilder where @@ -320,6 +326,7 @@ where /// Initialize a Session with an existing Runtime. /// This operation is used by the plugins to share the same Runtime as the router. +// ignore_tagging #[doc(hidden)] #[zenoh_macros::unstable] pub fn init(runtime: Runtime) -> InitBuilder { @@ -331,6 +338,7 @@ pub fn init(runtime: Runtime) -> InitBuilder { } /// A builder returned by [`init`] and used to initialize a Session with an existing Runtime. +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[doc(hidden)] #[zenoh_macros::unstable] @@ -343,12 +351,14 @@ pub struct InitBuilder { #[zenoh_macros::unstable] impl InitBuilder { #[inline] + // ignore_tagging pub fn aggregated_subscribers(mut self, exprs: Vec) -> Self { self.aggregated_subscribers = exprs; self } #[inline] + // ignore_tagging pub fn aggregated_publishers(mut self, exprs: Vec) -> Self { self.aggregated_publishers = exprs; self diff --git a/zenoh/src/liveliness.rs b/zenoh/src/liveliness.rs index 0883041bb7..74fcf52722 100644 --- a/zenoh/src/liveliness.rs +++ b/zenoh/src/liveliness.rs @@ -76,6 +76,7 @@ lazy_static::lazy_static!( /// .unwrap(); /// # }) /// ``` +// tags{liveliness} #[zenoh_macros::unstable] pub struct Liveliness<'a> { pub(crate) session: SessionRef<'a>, @@ -104,6 +105,7 @@ impl<'a> Liveliness<'a> { /// # }) /// ``` #[zenoh_macros::unstable] + // tags{liveliness.token.create} pub fn declare_token<'b, TryIntoKeyExpr>( &self, key_expr: TryIntoKeyExpr, @@ -139,6 +141,7 @@ impl<'a> Liveliness<'a> { /// } /// # }) /// ``` + // tags{liveliness.subscriber.create} #[zenoh_macros::unstable] pub fn declare_subscriber<'b, TryIntoKeyExpr>( &self, @@ -175,6 +178,7 @@ impl<'a> Liveliness<'a> { /// } /// # }) /// ``` + // tags{liveliness.query_token} #[zenoh_macros::unstable] pub fn get<'b: 'a, TryIntoKeyExpr>( &'a self, @@ -214,6 +218,7 @@ impl<'a> Liveliness<'a> { /// .unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[zenoh_macros::unstable] #[derive(Debug)] @@ -288,6 +293,7 @@ pub(crate) struct LivelinessTokenState { /// .unwrap(); /// # }) /// ``` +// tags{liveliness.token} #[zenoh_macros::unstable] #[derive(Debug)] pub struct LivelinessToken<'a> { @@ -314,6 +320,7 @@ pub struct LivelinessToken<'a> { /// liveliness.undeclare().res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[zenoh_macros::unstable] pub struct LivelinessTokenUndeclaration<'a> { @@ -366,6 +373,7 @@ impl<'a> LivelinessToken<'a> { /// liveliness.undeclare().res().await.unwrap(); /// # }) /// ``` + // tags{liveliness.token.undeclare} #[inline] pub fn undeclare(self) -> impl Resolve> + 'a { Undeclarable::undeclare_inner(self, ()) @@ -405,6 +413,7 @@ impl Drop for LivelinessToken<'_> { /// .unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[zenoh_macros::unstable] #[derive(Debug)] @@ -432,6 +441,7 @@ impl<'a, 'b> LivelinessSubscriberBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{liveliness.subscriber.callback} #[inline] #[zenoh_macros::unstable] pub fn callback( @@ -473,6 +483,7 @@ impl<'a, 'b> LivelinessSubscriberBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{liveliness.subscriber.callback} #[inline] #[zenoh_macros::unstable] pub fn callback_mut( @@ -504,6 +515,7 @@ impl<'a, 'b> LivelinessSubscriberBuilder<'a, 'b, DefaultHandler> { /// } /// # }) /// ``` + // tags{liveliness.subscriber.pipe} #[inline] #[zenoh_macros::unstable] pub fn with(self, handler: Handler) -> LivelinessSubscriberBuilder<'a, 'b, Handler> @@ -600,6 +612,7 @@ where /// } /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct LivelinessGetBuilder<'a, 'b, Handler> { @@ -627,6 +640,7 @@ impl<'a, 'b> LivelinessGetBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{liveliness.query_token.callback} #[inline] pub fn callback(self, callback: Callback) -> LivelinessGetBuilder<'a, 'b, Callback> where @@ -667,6 +681,7 @@ impl<'a, 'b> LivelinessGetBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{liveliness.query_token.callback} #[inline] pub fn callback_mut( self, @@ -698,6 +713,7 @@ impl<'a, 'b> LivelinessGetBuilder<'a, 'b, DefaultHandler> { /// } /// # }) /// ``` + // tags{liveliness.query_token.pipe} #[inline] pub fn with(self, handler: Handler) -> LivelinessGetBuilder<'a, 'b, Handler> where @@ -720,6 +736,7 @@ impl<'a, 'b> LivelinessGetBuilder<'a, 'b, DefaultHandler> { impl<'a, 'b, Handler> LivelinessGetBuilder<'a, 'b, Handler> { /// Set query timeout. + // tags{liveliness.query_token.timeout} #[inline] pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = timeout; diff --git a/zenoh/src/net/codec/mod.rs b/zenoh/src/net/codec/mod.rs index 06154cd02c..846d224139 100644 --- a/zenoh/src/net/codec/mod.rs +++ b/zenoh/src/net/codec/mod.rs @@ -11,12 +11,15 @@ // Contributors: // ZettaScale Zenoh Team, // +// ignore_tagging pub(crate) mod linkstate; +// ignore_tagging #[derive(Clone, Copy)] pub struct Zenoh080Routing; impl Zenoh080Routing { + // ignore_tagging pub const fn new() -> Self { Self } diff --git a/zenoh/src/net/protocol/mod.rs b/zenoh/src/net/protocol/mod.rs index 7343a07fd0..a00abbf8ae 100644 --- a/zenoh/src/net/protocol/mod.rs +++ b/zenoh/src/net/protocol/mod.rs @@ -11,4 +11,5 @@ // Contributors: // ZettaScale Zenoh Team, // +// ignore_tagging pub(crate) mod linkstate; diff --git a/zenoh/src/net/routing/face.rs b/zenoh/src/net/routing/face.rs new file mode 100644 index 0000000000..cfe5daec7e --- /dev/null +++ b/zenoh/src/net/routing/face.rs @@ -0,0 +1,459 @@ +// +// 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, +// +use super::pubsub::*; +use super::queries::*; +use super::router::*; +use std::collections::{HashMap, HashSet}; +use std::fmt; +use std::sync::Arc; +use zenoh_protocol::zenoh::RequestBody; +use zenoh_protocol::{ + core::{ExprId, WhatAmI, ZenohId}, + network::{ + declare::queryable::ext::QueryableInfo, Mapping, Push, Request, RequestId, Response, + ResponseFinal, + }, +}; +#[cfg(feature = "stats")] +use zenoh_transport::stats::TransportStats; +use zenoh_transport::{multicast::TransportMulticast, primitives::Primitives}; + +// ignore_tagging +pub struct FaceState { + pub(super) id: usize, + pub(super) zid: ZenohId, + pub(super) whatami: WhatAmI, + #[allow(dead_code)] + pub(super) local: bool, + #[cfg(feature = "stats")] + pub(super) stats: Option>, + pub(super) primitives: Arc, + pub(super) link_id: usize, + pub(super) local_mappings: HashMap>, + pub(super) remote_mappings: HashMap>, + pub(super) local_subs: HashSet>, + pub(super) remote_subs: HashSet>, + pub(super) local_qabls: HashMap, QueryableInfo>, + pub(super) remote_qabls: HashSet>, + pub(super) next_qid: RequestId, + pub(super) pending_queries: HashMap>, + pub(super) mcast_group: Option, +} + +impl FaceState { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + id: usize, + zid: ZenohId, + whatami: WhatAmI, + local: bool, + #[cfg(feature = "stats")] stats: Option>, + primitives: Arc, + link_id: usize, + mcast_group: Option, + ) -> Arc { + Arc::new(FaceState { + id, + zid, + whatami, + local, + #[cfg(feature = "stats")] + stats, + primitives, + link_id, + local_mappings: HashMap::new(), + remote_mappings: HashMap::new(), + local_subs: HashSet::new(), + remote_subs: HashSet::new(), + local_qabls: HashMap::new(), + remote_qabls: HashSet::new(), + next_qid: 0, + pending_queries: HashMap::new(), + mcast_group, + }) + } + + #[allow(dead_code)] + #[inline] + // ignore_tagging + pub fn is_local(&self) -> bool { + self.local + } + + #[inline] + #[allow(clippy::trivially_copy_pass_by_ref)] + pub(super) fn get_mapping( + &self, + prefixid: &ExprId, + mapping: Mapping, + ) -> Option<&std::sync::Arc> { + match mapping { + Mapping::Sender => self.remote_mappings.get(prefixid), + Mapping::Receiver => self.local_mappings.get(prefixid), + } + } + + pub(super) fn get_next_local_id(&self) -> ExprId { + let mut id = 1; + while self.local_mappings.get(&id).is_some() || self.remote_mappings.get(&id).is_some() { + id += 1; + } + id + } + + pub(super) fn get_router(&self, tables: &Tables, nodeid: &u64) -> Option { + match tables.routers_net.as_ref().unwrap().get_link(self.link_id) { + Some(link) => match link.get_zid(nodeid) { + Some(router) => Some(*router), + None => { + log::error!( + "Received router declaration with unknown routing context id {}", + nodeid + ); + None + } + }, + None => { + log::error!( + "Could not find corresponding link in routers network for {}", + self + ); + None + } + } + } + + pub(super) fn get_peer(&self, tables: &Tables, nodeid: &u64) -> Option { + match tables.peers_net.as_ref().unwrap().get_link(self.link_id) { + Some(link) => match link.get_zid(nodeid) { + Some(router) => Some(*router), + None => { + log::error!( + "Received peer declaration with unknown routing context id {}", + nodeid + ); + None + } + }, + None => { + log::error!( + "Could not find corresponding link in peers network for {}", + self + ); + None + } + } + } +} + +impl fmt::Display for FaceState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Face{{{}, {}}}", self.id, self.zid) + } +} + +// ignore_tagging +#[derive(Clone)] +pub struct Face { + pub(crate) tables: Arc, + pub(crate) state: Arc, +} + +impl Primitives for Face { + fn send_declare(&self, msg: zenoh_protocol::network::Declare) { + let ctrl_lock = zlock!(self.tables.ctrl_lock); + match msg.body { + zenoh_protocol::network::DeclareBody::DeclareKeyExpr(m) => { + register_expr(&self.tables, &mut self.state.clone(), m.id, &m.wire_expr); + } + zenoh_protocol::network::DeclareBody::UndeclareKeyExpr(m) => { + unregister_expr(&self.tables, &mut self.state.clone(), m.id); + } + zenoh_protocol::network::DeclareBody::DeclareSubscriber(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_router_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + router, + ) + } + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_peer_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + peer, + ) + } + } else { + declare_client_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + ) + } + } + _ => declare_client_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + ), + } + } + zenoh_protocol::network::DeclareBody::UndeclareSubscriber(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_router_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &router, + ) + } + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_peer_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &peer, + ) + } + } else { + forget_client_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + ) + } + } + _ => forget_client_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + ), + } + } + zenoh_protocol::network::DeclareBody::DeclareQueryable(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_router_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + router, + ) + } + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_peer_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + peer, + ) + } + } else { + declare_client_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + ) + } + } + _ => declare_client_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + ), + } + } + zenoh_protocol::network::DeclareBody::UndeclareQueryable(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_router_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &router, + ) + } + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_peer_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &peer, + ) + } + } else { + forget_client_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + ) + } + } + _ => forget_client_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + ), + } + } + zenoh_protocol::network::DeclareBody::DeclareToken(_m) => todo!(), + zenoh_protocol::network::DeclareBody::UndeclareToken(_m) => todo!(), + zenoh_protocol::network::DeclareBody::DeclareInterest(_m) => todo!(), + zenoh_protocol::network::DeclareBody::FinalInterest(_m) => todo!(), + zenoh_protocol::network::DeclareBody::UndeclareInterest(_m) => todo!(), + } + drop(ctrl_lock); + } + + fn send_push(&self, msg: Push) { + full_reentrant_route_data( + &self.tables.tables, + &self.state, + &msg.wire_expr, + msg.ext_qos, + msg.payload, + msg.ext_nodeid.node_id as u64, + ); + } + + fn send_request(&self, msg: Request) { + match msg.payload { + RequestBody::Query(_) => { + route_query( + &self.tables, + &self.state, + &msg.wire_expr, + // parameters, + msg.id, + msg.ext_target, + // consolidation, + msg.payload, + msg.ext_nodeid.node_id as u64, + ); + } + RequestBody::Pull(_) => { + pull_data(&self.tables.tables, &self.state.clone(), msg.wire_expr); + } + _ => { + log::error!("Unsupported request"); + } + } + } + + fn send_response(&self, msg: Response) { + route_send_response( + &self.tables, + &mut self.state.clone(), + msg.rid, + msg.ext_respid, + msg.wire_expr, + msg.payload, + ); + } + + fn send_response_final(&self, msg: ResponseFinal) { + route_send_response_final(&self.tables, &mut self.state.clone(), msg.rid); + } + + fn send_close(&self) { + super::router::close_face(&self.tables, &Arc::downgrade(&self.state)); + } +} + +impl fmt::Display for Face { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.state.fmt(f) + } +} diff --git a/zenoh/src/net/routing/mod.rs b/zenoh/src/net/routing/mod.rs index 0b069c1337..f6d00051ca 100644 --- a/zenoh/src/net/routing/mod.rs +++ b/zenoh/src/net/routing/mod.rs @@ -17,10 +17,14 @@ //! This module is intended for Zenoh's internal use. //! //! [Click here for Zenoh's documentation](../zenoh/index.html) -pub mod dispatcher; -pub mod hat; -pub mod interceptor; -pub mod router; +pub(crate) mod face; +pub(crate) mod network; +pub(crate) mod pubsub; +pub(crate) mod queries; +// ignore_tagging +pub(crate) mod resource; +// ignore_tagging +pub(crate) mod router; use std::{cell::OnceCell, sync::Arc}; diff --git a/zenoh/src/net/routing/pubsub.rs b/zenoh/src/net/routing/pubsub.rs new file mode 100644 index 0000000000..87001e1ad7 --- /dev/null +++ b/zenoh/src/net/routing/pubsub.rs @@ -0,0 +1,1957 @@ +// +// 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, +// +use super::face::FaceState; +use super::network::Network; +use super::resource::{ + DataRoutes, Direction, PullCaches, Resource, Route, RoutingContext, SessionContext, +}; +use super::router::{RoutingExpr, Tables, TablesLock}; +use petgraph::graph::NodeIndex; +use std::borrow::Cow; +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; +use std::sync::RwLock; +use std::sync::{Arc, RwLockReadGuard}; +use zenoh_core::zread; +use zenoh_protocol::{ + core::{ + key_expr::{keyexpr, OwnedKeyExpr}, + Reliability, WhatAmI, WireExpr, ZenohId, + }, + network::{ + declare::{ + common::ext::WireExprType, ext, subscriber::ext::SubscriberInfo, Declare, DeclareBody, + DeclareSubscriber, Mode, UndeclareSubscriber, + }, + Push, + }, + zenoh::PushBody, +}; +use zenoh_sync::get_mut_unchecked; + +#[inline] +fn send_sourced_subscription_to_net_childs( + tables: &Tables, + net: &Network, + childs: &[NodeIndex], + res: &Arc, + src_face: Option<&Arc>, + sub_info: &SubscriberInfo, + routing_context: Option, +) { + for child in childs { + if net.graph.contains_node(*child) { + match tables.get_face(&net.graph[*child].zid).cloned() { + Some(mut someface) => { + if src_face.is_none() || someface.id != src_face.unwrap().id { + let key_expr = Resource::decl_key(res, &mut someface); + + log::debug!("Send subscription {} on {}", res.expr(), someface); + + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: *sub_info, + }), + }); + } + } + None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), + } + } + } +} + +#[inline] +fn propagate_simple_subscription_to( + tables: &mut Tables, + dst_face: &mut Arc, + res: &Arc, + sub_info: &SubscriberInfo, + src_face: &mut Arc, + full_peer_net: bool, +) { + if (src_face.id != dst_face.id || res.expr().starts_with(super::PREFIX_LIVELINESS)) + && !dst_face.local_subs.contains(res) + && match tables.whatami { + WhatAmI::Router => { + if full_peer_net { + dst_face.whatami == WhatAmI::Client + } else { + dst_face.whatami != WhatAmI::Router + && (src_face.whatami != WhatAmI::Peer + || dst_face.whatami != WhatAmI::Peer + || tables.failover_brokering(src_face.zid, dst_face.zid)) + } + } + WhatAmI::Peer => { + if full_peer_net { + dst_face.whatami == WhatAmI::Client + } else { + src_face.whatami == WhatAmI::Client || dst_face.whatami == WhatAmI::Client + } + } + _ => src_face.whatami == WhatAmI::Client || dst_face.whatami == WhatAmI::Client, + } + { + get_mut_unchecked(dst_face).local_subs.insert(res.clone()); + let key_expr = Resource::decl_key(res, dst_face); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: *sub_info, + }), + }); + } +} + +fn propagate_simple_subscription( + tables: &mut Tables, + res: &Arc, + sub_info: &SubscriberInfo, + src_face: &mut Arc, +) { + let full_peer_net = tables.full_net(WhatAmI::Peer); + for mut dst_face in tables + .faces + .values() + .cloned() + .collect::>>() + { + propagate_simple_subscription_to( + tables, + &mut dst_face, + res, + sub_info, + src_face, + full_peer_net, + ); + } +} + +fn propagate_sourced_subscription( + tables: &Tables, + res: &Arc, + sub_info: &SubscriberInfo, + src_face: Option<&Arc>, + source: &ZenohId, + net_type: WhatAmI, +) { + let net = tables.get_net(net_type).unwrap(); + match net.get_idx(source) { + Some(tree_sid) => { + if net.trees.len() > tree_sid.index() { + send_sourced_subscription_to_net_childs( + tables, + net, + &net.trees[tree_sid.index()].childs, + res, + src_face, + sub_info, + Some(tree_sid.index() as u16), + ); + } else { + log::trace!( + "Propagating sub {}: tree for node {} sid:{} not yet ready", + res.expr(), + tree_sid.index(), + source + ); + } + } + None => log::error!( + "Error propagating sub {}: cannot get index of {}!", + res.expr(), + source + ), + } +} + +fn register_router_subscription( + tables: &mut Tables, + face: &mut Arc, + res: &mut Arc, + sub_info: &SubscriberInfo, + router: ZenohId, +) { + if !res.context().router_subs.contains(&router) { + // Register router subscription + { + log::debug!( + "Register router subscription {} (router: {})", + res.expr(), + router + ); + get_mut_unchecked(res) + .context_mut() + .router_subs + .insert(router); + tables.router_subs.insert(res.clone()); + } + + // Propagate subscription to routers + propagate_sourced_subscription(tables, res, sub_info, Some(face), &router, WhatAmI::Router); + } + // Propagate subscription to peers + if tables.full_net(WhatAmI::Peer) && face.whatami != WhatAmI::Peer { + register_peer_subscription(tables, face, res, sub_info, tables.zid) + } + + // Propagate subscription to clients + propagate_simple_subscription(tables, res, sub_info, face); +} + +pub(crate) fn declare_router_subscription( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + sub_info: &SubscriberInfo, + router: ZenohId, +) { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { + Some(mut prefix) => { + let res = Resource::get_resource(&prefix, &expr.suffix); + let (mut res, mut wtables) = + if res.as_ref().map(|r| r.context.is_some()).unwrap_or(false) { + drop(rtables); + let wtables = zwrite!(tables.tables); + (res.unwrap(), wtables) + } else { + let mut fullexpr = prefix.expr(); + fullexpr.push_str(expr.suffix.as_ref()); + let mut matches = keyexpr::new(fullexpr.as_str()) + .map(|ke| Resource::get_matches(&rtables, ke)) + .unwrap_or_default(); + drop(rtables); + let mut wtables = zwrite!(tables.tables); + let mut res = + Resource::make_resource(&mut wtables, &mut prefix, expr.suffix.as_ref()); + matches.push(Arc::downgrade(&res)); + Resource::match_resource(&wtables, &mut res, matches); + (res, wtables) + }; + register_router_subscription(&mut wtables, face, &mut res, sub_info, router); + disable_matches_data_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_data_routes = compute_matches_data_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + drop(wtables); + } + None => log::error!( + "Declare router subscription for unknown scope {}!", + expr.scope + ), + } +} + +fn register_peer_subscription( + tables: &mut Tables, + face: &mut Arc, + res: &mut Arc, + sub_info: &SubscriberInfo, + peer: ZenohId, +) { + if !res.context().peer_subs.contains(&peer) { + // Register peer subscription + { + log::debug!("Register peer subscription {} (peer: {})", res.expr(), peer); + get_mut_unchecked(res).context_mut().peer_subs.insert(peer); + tables.peer_subs.insert(res.clone()); + } + + // Propagate subscription to peers + propagate_sourced_subscription(tables, res, sub_info, Some(face), &peer, WhatAmI::Peer); + } + + if tables.whatami == WhatAmI::Peer { + // Propagate subscription to clients + propagate_simple_subscription(tables, res, sub_info, face); + } +} + +pub(crate) fn declare_peer_subscription( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + sub_info: &SubscriberInfo, + peer: ZenohId, +) { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { + Some(mut prefix) => { + let res = Resource::get_resource(&prefix, &expr.suffix); + let (mut res, mut wtables) = + if res.as_ref().map(|r| r.context.is_some()).unwrap_or(false) { + drop(rtables); + let wtables = zwrite!(tables.tables); + (res.unwrap(), wtables) + } else { + let mut fullexpr = prefix.expr(); + fullexpr.push_str(expr.suffix.as_ref()); + let mut matches = keyexpr::new(fullexpr.as_str()) + .map(|ke| Resource::get_matches(&rtables, ke)) + .unwrap_or_default(); + drop(rtables); + let mut wtables = zwrite!(tables.tables); + let mut res = + Resource::make_resource(&mut wtables, &mut prefix, expr.suffix.as_ref()); + matches.push(Arc::downgrade(&res)); + Resource::match_resource(&wtables, &mut res, matches); + (res, wtables) + }; + register_peer_subscription(&mut wtables, face, &mut res, sub_info, peer); + if wtables.whatami == WhatAmI::Router { + let mut propa_sub_info = *sub_info; + propa_sub_info.mode = Mode::Push; + let zid = wtables.zid; + register_router_subscription(&mut wtables, face, &mut res, &propa_sub_info, zid); + } + disable_matches_data_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_data_routes = compute_matches_data_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + drop(wtables); + } + None => log::error!( + "Declare router subscription for unknown scope {}!", + expr.scope + ), + } +} + +fn register_client_subscription( + _tables: &mut Tables, + face: &mut Arc, + res: &mut Arc, + sub_info: &SubscriberInfo, +) { + // Register subscription + { + let res = get_mut_unchecked(res); + log::debug!("Register subscription {} for {}", res.expr(), face); + match res.session_ctxs.get_mut(&face.id) { + Some(ctx) => match &ctx.subs { + Some(info) => { + if Mode::Pull == info.mode { + get_mut_unchecked(ctx).subs = Some(*sub_info); + } + } + None => { + get_mut_unchecked(ctx).subs = Some(*sub_info); + } + }, + None => { + res.session_ctxs.insert( + face.id, + Arc::new(SessionContext { + face: face.clone(), + local_expr_id: None, + remote_expr_id: None, + subs: Some(*sub_info), + qabl: None, + last_values: HashMap::new(), + }), + ); + } + } + } + get_mut_unchecked(face).remote_subs.insert(res.clone()); +} + +pub(crate) fn declare_client_subscription( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + sub_info: &SubscriberInfo, +) { + log::debug!("Register client subscription"); + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { + Some(mut prefix) => { + let res = Resource::get_resource(&prefix, &expr.suffix); + let (mut res, mut wtables) = + if res.as_ref().map(|r| r.context.is_some()).unwrap_or(false) { + drop(rtables); + let wtables = zwrite!(tables.tables); + (res.unwrap(), wtables) + } else { + let mut fullexpr = prefix.expr(); + fullexpr.push_str(expr.suffix.as_ref()); + let mut matches = keyexpr::new(fullexpr.as_str()) + .map(|ke| Resource::get_matches(&rtables, ke)) + .unwrap_or_default(); + drop(rtables); + let mut wtables = zwrite!(tables.tables); + let mut res = + Resource::make_resource(&mut wtables, &mut prefix, expr.suffix.as_ref()); + matches.push(Arc::downgrade(&res)); + Resource::match_resource(&wtables, &mut res, matches); + (res, wtables) + }; + + register_client_subscription(&mut wtables, face, &mut res, sub_info); + let mut propa_sub_info = *sub_info; + propa_sub_info.mode = Mode::Push; + match wtables.whatami { + WhatAmI::Router => { + let zid = wtables.zid; + register_router_subscription( + &mut wtables, + face, + &mut res, + &propa_sub_info, + zid, + ); + } + WhatAmI::Peer => { + if wtables.full_net(WhatAmI::Peer) { + let zid = wtables.zid; + register_peer_subscription( + &mut wtables, + face, + &mut res, + &propa_sub_info, + zid, + ); + } else { + propagate_simple_subscription(&mut wtables, &res, &propa_sub_info, face); + // This introduced a buffer overflow on windows + // TODO: Let's deactivate this on windows until Fixed + #[cfg(not(windows))] + for mcast_group in &wtables.mcast_groups { + mcast_group.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: res.expr().into(), + ext_info: *sub_info, + }), + }) + } + } + } + _ => { + propagate_simple_subscription(&mut wtables, &res, &propa_sub_info, face); + // This introduced a buffer overflow on windows + // TODO: Let's deactivate this on windows until Fixed + #[cfg(not(windows))] + for mcast_group in &wtables.mcast_groups { + mcast_group.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: res.expr().into(), + ext_info: *sub_info, + }), + }) + } + } + } + disable_matches_data_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_data_routes = compute_matches_data_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + drop(wtables); + } + None => log::error!("Declare subscription for unknown scope {}!", expr.scope), + } +} + +#[inline] +fn remote_router_subs(tables: &Tables, res: &Arc) -> bool { + res.context.is_some() + && res + .context() + .router_subs + .iter() + .any(|peer| peer != &tables.zid) +} + +#[inline] +fn remote_peer_subs(tables: &Tables, res: &Arc) -> bool { + res.context.is_some() + && res + .context() + .peer_subs + .iter() + .any(|peer| peer != &tables.zid) +} + +#[inline] +fn client_subs(res: &Arc) -> Vec> { + res.session_ctxs + .values() + .filter_map(|ctx| { + if ctx.subs.is_some() { + Some(ctx.face.clone()) + } else { + None + } + }) + .collect() +} + +#[inline] +fn send_forget_sourced_subscription_to_net_childs( + tables: &Tables, + net: &Network, + childs: &[NodeIndex], + res: &Arc, + src_face: Option<&Arc>, + routing_context: Option, +) { + for child in childs { + if net.graph.contains_node(*child) { + match tables.get_face(&net.graph[*child].zid).cloned() { + Some(mut someface) => { + if src_face.is_none() || someface.id != src_face.unwrap().id { + let wire_expr = Resource::decl_key(res, &mut someface); + + log::debug!("Send forget subscription {} on {}", res.expr(), someface); + + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + } + } + None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), + } + } + } +} + +fn propagate_forget_simple_subscription(tables: &mut Tables, res: &Arc) { + for face in tables.faces.values_mut() { + if face.local_subs.contains(res) { + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + get_mut_unchecked(face).local_subs.remove(res); + } + } +} + +fn propagate_forget_simple_subscription_to_peers(tables: &mut Tables, res: &Arc) { + if !tables.full_net(WhatAmI::Peer) + && res.context().router_subs.len() == 1 + && res.context().router_subs.contains(&tables.zid) + { + for mut face in tables + .faces + .values() + .cloned() + .collect::>>() + { + if face.whatami == WhatAmI::Peer + && face.local_subs.contains(res) + && !res.session_ctxs.values().any(|s| { + face.zid != s.face.zid + && s.subs.is_some() + && (s.face.whatami == WhatAmI::Client + || (s.face.whatami == WhatAmI::Peer + && tables.failover_brokering(s.face.zid, face.zid))) + }) + { + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + + get_mut_unchecked(&mut face).local_subs.remove(res); + } + } + } +} + +fn propagate_forget_sourced_subscription( + tables: &Tables, + res: &Arc, + src_face: Option<&Arc>, + source: &ZenohId, + net_type: WhatAmI, +) { + let net = tables.get_net(net_type).unwrap(); + match net.get_idx(source) { + Some(tree_sid) => { + if net.trees.len() > tree_sid.index() { + send_forget_sourced_subscription_to_net_childs( + tables, + net, + &net.trees[tree_sid.index()].childs, + res, + src_face, + Some(tree_sid.index() as u16), + ); + } else { + log::trace!( + "Propagating forget sub {}: tree for node {} sid:{} not yet ready", + res.expr(), + tree_sid.index(), + source + ); + } + } + None => log::error!( + "Error propagating forget sub {}: cannot get index of {}!", + res.expr(), + source + ), + } +} + +fn unregister_router_subscription(tables: &mut Tables, res: &mut Arc, router: &ZenohId) { + log::debug!( + "Unregister router subscription {} (router: {})", + res.expr(), + router + ); + get_mut_unchecked(res) + .context_mut() + .router_subs + .retain(|sub| sub != router); + + if res.context().router_subs.is_empty() { + tables.router_subs.retain(|sub| !Arc::ptr_eq(sub, res)); + + if tables.full_net(WhatAmI::Peer) { + undeclare_peer_subscription(tables, None, res, &tables.zid.clone()); + } + propagate_forget_simple_subscription(tables, res); + } + + propagate_forget_simple_subscription_to_peers(tables, res); +} + +fn undeclare_router_subscription( + tables: &mut Tables, + face: Option<&Arc>, + res: &mut Arc, + router: &ZenohId, +) { + if res.context().router_subs.contains(router) { + unregister_router_subscription(tables, res, router); + propagate_forget_sourced_subscription(tables, res, face, router, WhatAmI::Router); + } +} + +pub(crate) fn forget_router_subscription( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + router: &ZenohId, +) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + drop(rtables); + let mut wtables = zwrite!(tables.tables); + undeclare_router_subscription(&mut wtables, Some(face), &mut res, router); + disable_matches_data_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_data_routes = compute_matches_data_routes_(&rtables, &res); + drop(rtables); + let wtables = zwrite!(tables.tables); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + Resource::clean(&mut res); + drop(wtables); + } + None => log::error!("Undeclare unknown router subscription!"), + }, + None => log::error!("Undeclare router subscription with unknown scope!"), + } +} + +fn unregister_peer_subscription(tables: &mut Tables, res: &mut Arc, peer: &ZenohId) { + log::debug!( + "Unregister peer subscription {} (peer: {})", + res.expr(), + peer + ); + get_mut_unchecked(res) + .context_mut() + .peer_subs + .retain(|sub| sub != peer); + + if res.context().peer_subs.is_empty() { + tables.peer_subs.retain(|sub| !Arc::ptr_eq(sub, res)); + + if tables.whatami == WhatAmI::Peer { + propagate_forget_simple_subscription(tables, res); + } + } +} + +fn undeclare_peer_subscription( + tables: &mut Tables, + face: Option<&Arc>, + res: &mut Arc, + peer: &ZenohId, +) { + if res.context().peer_subs.contains(peer) { + unregister_peer_subscription(tables, res, peer); + propagate_forget_sourced_subscription(tables, res, face, peer, WhatAmI::Peer); + } +} + +pub(crate) fn forget_peer_subscription( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + peer: &ZenohId, +) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + drop(rtables); + let mut wtables = zwrite!(tables.tables); + undeclare_peer_subscription(&mut wtables, Some(face), &mut res, peer); + if wtables.whatami == WhatAmI::Router { + let client_subs = res.session_ctxs.values().any(|ctx| ctx.subs.is_some()); + let peer_subs = remote_peer_subs(&wtables, &res); + let zid = wtables.zid; + if !client_subs && !peer_subs { + undeclare_router_subscription(&mut wtables, None, &mut res, &zid); + } + } + disable_matches_data_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_data_routes = compute_matches_data_routes_(&rtables, &res); + drop(rtables); + let wtables = zwrite!(tables.tables); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + Resource::clean(&mut res); + drop(wtables); + } + None => log::error!("Undeclare unknown peer subscription!"), + }, + None => log::error!("Undeclare peer subscription with unknown scope!"), + } +} + +pub(crate) fn undeclare_client_subscription( + tables: &mut Tables, + face: &mut Arc, + res: &mut Arc, +) { + log::debug!("Unregister client subscription {} for {}", res.expr(), face); + if let Some(ctx) = get_mut_unchecked(res).session_ctxs.get_mut(&face.id) { + get_mut_unchecked(ctx).subs = None; + } + get_mut_unchecked(face).remote_subs.remove(res); + + let mut client_subs = client_subs(res); + let router_subs = remote_router_subs(tables, res); + let peer_subs = remote_peer_subs(tables, res); + match tables.whatami { + WhatAmI::Router => { + if client_subs.is_empty() && !peer_subs { + undeclare_router_subscription(tables, None, res, &tables.zid.clone()); + } else { + propagate_forget_simple_subscription_to_peers(tables, res); + } + } + WhatAmI::Peer => { + if client_subs.is_empty() { + if tables.full_net(WhatAmI::Peer) { + undeclare_peer_subscription(tables, None, res, &tables.zid.clone()); + } else { + propagate_forget_simple_subscription(tables, res); + } + } + } + _ => { + if client_subs.is_empty() { + propagate_forget_simple_subscription(tables, res); + } + } + } + if client_subs.len() == 1 && !router_subs && !peer_subs { + let face = &mut client_subs[0]; + if face.local_subs.contains(res) + && !(face.whatami == WhatAmI::Client + && res.expr().starts_with(super::PREFIX_LIVELINESS)) + { + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + + get_mut_unchecked(face).local_subs.remove(res); + } + } +} + +pub(crate) fn forget_client_subscription( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, +) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + drop(rtables); + let mut wtables = zwrite!(tables.tables); + undeclare_client_subscription(&mut wtables, face, &mut res); + disable_matches_data_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_data_routes = compute_matches_data_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + Resource::clean(&mut res); + drop(wtables); + } + None => log::error!("Undeclare unknown subscription!"), + }, + None => log::error!("Undeclare subscription with unknown scope!"), + } +} + +pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { + let sub_info = SubscriberInfo { + reliability: Reliability::Reliable, // @TODO + mode: Mode::Push, + }; + match tables.whatami { + WhatAmI::Router => { + if face.whatami == WhatAmI::Client { + for sub in &tables.router_subs { + get_mut_unchecked(face).local_subs.insert(sub.clone()); + let key_expr = Resource::decl_key(sub, face); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); + } + } else if face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + for sub in &tables.router_subs { + if sub.context.is_some() + && (sub.context().router_subs.iter().any(|r| *r != tables.zid) + || sub.session_ctxs.values().any(|s| { + s.subs.is_some() + && (s.face.whatami == WhatAmI::Client + || (s.face.whatami == WhatAmI::Peer + && tables.failover_brokering(s.face.zid, face.zid))) + })) + { + get_mut_unchecked(face).local_subs.insert(sub.clone()); + let key_expr = Resource::decl_key(sub, face); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); + } + } + } + } + WhatAmI::Peer => { + if tables.full_net(WhatAmI::Peer) { + if face.whatami == WhatAmI::Client { + for sub in &tables.peer_subs { + get_mut_unchecked(face).local_subs.insert(sub.clone()); + let key_expr = Resource::decl_key(sub, face); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); + } + } + } else { + for src_face in tables + .faces + .values() + .cloned() + .collect::>>() + { + for sub in &src_face.remote_subs { + propagate_simple_subscription_to( + tables, + face, + sub, + &sub_info, + &mut src_face.clone(), + false, + ); + } + } + } + } + WhatAmI::Client => { + for src_face in tables + .faces + .values() + .cloned() + .collect::>>() + { + for sub in &src_face.remote_subs { + propagate_simple_subscription_to( + tables, + face, + sub, + &sub_info, + &mut src_face.clone(), + false, + ); + } + } + } + } +} + +pub(crate) fn pubsub_remove_node(tables: &mut Tables, node: &ZenohId, net_type: WhatAmI) { + match net_type { + WhatAmI::Router => { + for mut res in tables + .router_subs + .iter() + .filter(|res| res.context().router_subs.contains(node)) + .cloned() + .collect::>>() + { + unregister_router_subscription(tables, &mut res, node); + + let matches_data_routes = compute_matches_data_routes_(tables, &res); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + Resource::clean(&mut res) + } + } + WhatAmI::Peer => { + for mut res in tables + .peer_subs + .iter() + .filter(|res| res.context().peer_subs.contains(node)) + .cloned() + .collect::>>() + { + unregister_peer_subscription(tables, &mut res, node); + + if tables.whatami == WhatAmI::Router { + let client_subs = res.session_ctxs.values().any(|ctx| ctx.subs.is_some()); + let peer_subs = remote_peer_subs(tables, &res); + if !client_subs && !peer_subs { + undeclare_router_subscription(tables, None, &mut res, &tables.zid.clone()); + } + } + + // compute_matches_data_routes(tables, &mut res); + let matches_data_routes = compute_matches_data_routes_(tables, &res); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + } + Resource::clean(&mut res) + } + } + _ => (), + } +} + +pub(crate) fn pubsub_tree_change( + tables: &mut Tables, + new_childs: &[Vec], + net_type: WhatAmI, +) { + // propagate subs to new childs + for (tree_sid, tree_childs) in new_childs.iter().enumerate() { + if !tree_childs.is_empty() { + let net = tables.get_net(net_type).unwrap(); + let tree_idx = NodeIndex::new(tree_sid); + if net.graph.contains_node(tree_idx) { + let tree_id = net.graph[tree_idx].zid; + + let subs_res = match net_type { + WhatAmI::Router => &tables.router_subs, + _ => &tables.peer_subs, + }; + + for res in subs_res { + let subs = match net_type { + WhatAmI::Router => &res.context().router_subs, + _ => &res.context().peer_subs, + }; + for sub in subs { + if *sub == tree_id { + let sub_info = SubscriberInfo { + reliability: Reliability::Reliable, // @TODO + mode: Mode::Push, + }; + send_sourced_subscription_to_net_childs( + tables, + net, + tree_childs, + res, + None, + &sub_info, + Some(tree_sid as u16), + ); + } + } + } + } + } + } + + // recompute routes + compute_data_routes_from(tables, &mut tables.root_res.clone()); +} + +pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: &[ZenohId]) { + if let Some(src_face) = tables.get_face(zid).cloned() { + if tables.router_peers_failover_brokering + && tables.whatami == WhatAmI::Router + && src_face.whatami == WhatAmI::Peer + { + for res in &src_face.remote_subs { + let client_subs = res + .session_ctxs + .values() + .any(|ctx| ctx.face.whatami == WhatAmI::Client && ctx.subs.is_some()); + if !remote_router_subs(tables, res) && !client_subs { + for ctx in get_mut_unchecked(&mut res.clone()) + .session_ctxs + .values_mut() + { + let dst_face = &mut get_mut_unchecked(ctx).face; + if dst_face.whatami == WhatAmI::Peer && src_face.zid != dst_face.zid { + if dst_face.local_subs.contains(res) { + let forget = !Tables::failover_brokering_to(links, dst_face.zid) + && { + let ctx_links = tables + .peers_net + .as_ref() + .map(|net| net.get_links(dst_face.zid)) + .unwrap_or_else(|| &[]); + res.session_ctxs.values().any(|ctx2| { + ctx2.face.whatami == WhatAmI::Peer + && ctx2.subs.is_some() + && Tables::failover_brokering_to( + ctx_links, + ctx2.face.zid, + ) + }) + }; + if forget { + let wire_expr = Resource::get_best_key(res, "", dst_face.id); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber( + UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }, + ), + }); + + get_mut_unchecked(dst_face).local_subs.remove(res); + } + } else if Tables::failover_brokering_to(links, ctx.face.zid) { + let dst_face = &mut get_mut_unchecked(ctx).face; + get_mut_unchecked(dst_face).local_subs.insert(res.clone()); + let key_expr = Resource::decl_key(res, dst_face); + let sub_info = SubscriberInfo { + reliability: Reliability::Reliable, // TODO + mode: Mode::Push, + }; + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); + } + } + } + } + } + } + } +} + +#[inline] +fn insert_faces_for_subs( + route: &mut Route, + expr: &RoutingExpr, + tables: &Tables, + net: &Network, + source: usize, + subs: &HashSet, +) { + if net.trees.len() > source { + for sub in subs { + if let Some(sub_idx) = net.get_idx(sub) { + if net.trees[source].directions.len() > sub_idx.index() { + if let Some(direction) = net.trees[source].directions[sub_idx.index()] { + if net.graph.contains_node(direction) { + if let Some(face) = tables.get_face(&net.graph[direction].zid) { + route.entry(face.id).or_insert_with(|| { + let key_expr = + Resource::get_best_key(expr.prefix, expr.suffix, face.id); + ( + face.clone(), + key_expr.to_owned(), + if source != 0 { + Some(source as u16) + } else { + None + }, + ) + }); + } + } + } + } + } + } + } else { + log::trace!("Tree for node sid:{} not yet ready", source); + } +} + +fn compute_data_route( + tables: &Tables, + expr: &mut RoutingExpr, + source: Option, + source_type: WhatAmI, +) -> Arc { + let mut route = HashMap::new(); + let key_expr = expr.full_expr(); + if key_expr.ends_with('/') { + return Arc::new(route); + } + log::trace!( + "compute_data_route({}, {:?}, {:?})", + key_expr, + source, + source_type + ); + let key_expr = match OwnedKeyExpr::try_from(key_expr) { + Ok(ke) => ke, + Err(e) => { + log::warn!("Invalid KE reached the system: {}", e); + return Arc::new(route); + } + }; + let res = Resource::get_resource(expr.prefix, expr.suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); + + let master = tables.whatami != WhatAmI::Router + || !tables.full_net(WhatAmI::Peer) + || *tables.elect_router(&key_expr, tables.shared_nodes.iter()) == tables.zid; + + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + if tables.whatami == WhatAmI::Router { + if master || source_type == WhatAmI::Router { + let net = tables.routers_net.as_ref().unwrap(); + let router_source = match source_type { + WhatAmI::Router => source.unwrap(), + _ => net.idx.index(), + }; + insert_faces_for_subs( + &mut route, + expr, + tables, + net, + router_source, + &mres.context().router_subs, + ); + } + + if (master || source_type != WhatAmI::Router) && tables.full_net(WhatAmI::Peer) { + let net = tables.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_faces_for_subs( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_subs, + ); + } + } + + if tables.whatami == WhatAmI::Peer && tables.full_net(WhatAmI::Peer) { + let net = tables.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Router | WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_faces_for_subs( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_subs, + ); + } + + if tables.whatami != WhatAmI::Router || master || source_type == WhatAmI::Router { + for (sid, context) in &mres.session_ctxs { + if let Some(subinfo) = &context.subs { + if match tables.whatami { + WhatAmI::Router => context.face.whatami != WhatAmI::Router, + _ => { + source_type == WhatAmI::Client + || context.face.whatami == WhatAmI::Client + } + } && subinfo.mode == Mode::Push + { + route.entry(*sid).or_insert_with(|| { + let key_expr = Resource::get_best_key(expr.prefix, expr.suffix, *sid); + (context.face.clone(), key_expr.to_owned(), None) + }); + } + } + } + } + } + for mcast_group in &tables.mcast_groups { + route.insert( + mcast_group.id, + ( + mcast_group.clone(), + expr.full_expr().to_string().into(), + None, + ), + ); + } + Arc::new(route) +} + +fn compute_matching_pulls(tables: &Tables, expr: &mut RoutingExpr) -> Arc { + let mut pull_caches = vec![]; + let ke = if let Ok(ke) = OwnedKeyExpr::try_from(expr.full_expr()) { + ke + } else { + return Arc::new(pull_caches); + }; + let res = Resource::get_resource(expr.prefix, expr.suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &ke))); + + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + for context in mres.session_ctxs.values() { + if let Some(subinfo) = &context.subs { + if subinfo.mode == Mode::Pull { + pull_caches.push(context.clone()); + } + } + } + } + Arc::new(pull_caches) +} + +pub(super) fn compute_data_routes_(tables: &Tables, res: &Arc) -> DataRoutes { + let mut routes = DataRoutes { + matching_pulls: None, + routers_data_routes: vec![], + peers_data_routes: vec![], + peer_data_route: None, + client_data_route: None, + }; + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .routers_data_routes + .resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + routes.routers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + if !tables.full_net(WhatAmI::Peer) { + routes.peer_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.full_net(WhatAmI::Peer) + { + let indexes = tables + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .peers_data_routes + .resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + routes.peers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + routes.client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + routes.peer_data_route = Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + routes.client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + } + routes.matching_pulls = Some(compute_matching_pulls(tables, &mut expr)); + routes +} + +pub(crate) fn compute_data_routes(tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + let mut res_mut = res.clone(); + let res_mut = get_mut_unchecked(&mut res_mut); + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let routers_data_routes = &mut res_mut.context_mut().routers_data_routes; + routers_data_routes.clear(); + routers_data_routes.resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + routers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + if !tables.full_net(WhatAmI::Peer) { + res_mut.context_mut().peer_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.full_net(WhatAmI::Peer) + { + let indexes = tables + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let peers_data_routes = &mut res_mut.context_mut().peers_data_routes; + peers_data_routes.clear(); + peers_data_routes.resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + peers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + res_mut.context_mut().client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + res_mut.context_mut().peer_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + res_mut.context_mut().client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + } + res_mut.context_mut().matching_pulls = compute_matching_pulls(tables, &mut expr); + } +} + +pub(super) fn compute_data_routes_from(tables: &mut Tables, res: &mut Arc) { + compute_data_routes(tables, res); + let res = get_mut_unchecked(res); + for child in res.childs.values_mut() { + compute_data_routes_from(tables, child); + } +} + +pub(super) fn compute_matches_data_routes_<'a>( + tables: &'a Tables, + res: &'a Arc, +) -> Vec<(Arc, DataRoutes)> { + let mut routes = vec![]; + if res.context.is_some() { + routes.push((res.clone(), compute_data_routes_(tables, res))); + for match_ in &res.context().matches { + let match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + let match_routes = compute_data_routes_(tables, &match_); + routes.push((match_, match_routes)); + } + } + } + routes +} + +pub(super) fn disable_matches_data_routes(_tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + get_mut_unchecked(res).context_mut().valid_data_routes = false; + for match_ in &res.context().matches { + let mut match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + get_mut_unchecked(&mut match_) + .context_mut() + .valid_data_routes = false; + } + } + } +} + +macro_rules! treat_timestamp { + ($hlc:expr, $payload:expr, $drop:expr) => { + // if an HLC was configured (via Config.add_timestamp), + // check DataInfo and add a timestamp if there isn't + if let Some(hlc) = $hlc { + if let PushBody::Put(data) = &mut $payload { + if let Some(ref ts) = data.timestamp { + // Timestamp is present; update HLC with it (possibly raising error if delta exceed) + match hlc.update_with_timestamp(ts) { + Ok(()) => (), + Err(e) => { + if $drop { + log::error!( + "Error treating timestamp for received Data ({}). Drop it!", + e + ); + return; + } else { + data.timestamp = Some(hlc.new_timestamp()); + log::error!( + "Error treating timestamp for received Data ({}). Replace timestamp: {:?}", + e, + data.timestamp); + } + } + } + } else { + // Timestamp not present; add one + data.timestamp = Some(hlc.new_timestamp()); + log::trace!("Adding timestamp to DataInfo: {:?}", data.timestamp); + } + } + } + } +} + +#[inline] +pub(crate) fn get_data_route( + tables: &Tables, + whatami: WhatAmI, + link_id: usize, + res: &Option>, + expr: &mut RoutingExpr, + routing_context: u64, +) -> Arc { + match tables.whatami { + WhatAmI::Router => match whatami { + WhatAmI::Router => { + let routers_net = tables.routers_net.as_ref().unwrap(); + let local_context = routers_net.get_local_context(routing_context, link_id); + res.as_ref() + .and_then(|res| res.routers_data_route(local_context)) + .unwrap_or_else(|| { + compute_data_route(tables, expr, Some(local_context), whatami) + }) + } + WhatAmI::Peer => { + if tables.full_net(WhatAmI::Peer) { + let peers_net = tables.peers_net.as_ref().unwrap(); + let local_context = peers_net.get_local_context(routing_context, link_id); + res.as_ref() + .and_then(|res| res.peers_data_route(local_context)) + .unwrap_or_else(|| { + compute_data_route(tables, expr, Some(local_context), whatami) + }) + } else { + res.as_ref() + .and_then(|res| res.peer_data_route()) + .unwrap_or_else(|| compute_data_route(tables, expr, None, whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.routers_data_route(0)) + .unwrap_or_else(|| compute_data_route(tables, expr, None, whatami)), + }, + WhatAmI::Peer => { + if tables.full_net(WhatAmI::Peer) { + match whatami { + WhatAmI::Router | WhatAmI::Peer => { + let peers_net = tables.peers_net.as_ref().unwrap(); + let local_context = peers_net.get_local_context(routing_context, link_id); + res.as_ref() + .and_then(|res| res.peers_data_route(local_context)) + .unwrap_or_else(|| { + compute_data_route(tables, expr, Some(local_context), whatami) + }) + } + _ => res + .as_ref() + .and_then(|res| res.peers_data_route(0)) + .unwrap_or_else(|| compute_data_route(tables, expr, None, whatami)), + } + } else { + res.as_ref() + .and_then(|res| match whatami { + WhatAmI::Client => res.client_data_route(), + _ => res.peer_data_route(), + }) + .unwrap_or_else(|| compute_data_route(tables, expr, None, whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.client_data_route()) + .unwrap_or_else(|| compute_data_route(tables, expr, None, whatami)), + } +} + +#[inline] +fn get_matching_pulls( + tables: &Tables, + res: &Option>, + expr: &mut RoutingExpr, +) -> Arc { + res.as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| ctx.matching_pulls.clone()) + .unwrap_or_else(|| compute_matching_pulls(tables, expr)) +} + +macro_rules! cache_data { + ( + $matching_pulls:expr, + $expr:expr, + $payload:expr + ) => { + for context in $matching_pulls.iter() { + get_mut_unchecked(&mut context.clone()) + .last_values + .insert($expr.full_expr().to_string(), $payload.clone()); + } + }; +} + +#[inline] +fn should_route( + tables: &Tables, + src_face: &FaceState, + outface: &Arc, + expr: &mut RoutingExpr, +) -> bool { + if src_face.id != outface.id + && match (src_face.mcast_group.as_ref(), outface.mcast_group.as_ref()) { + (Some(l), Some(r)) => l != r, + _ => true, + } + { + let dst_master = tables.whatami != WhatAmI::Router + || outface.whatami != WhatAmI::Peer + || tables.peers_net.is_none() + || tables.zid + == *tables.elect_router(expr.full_expr(), tables.get_router_links(outface.zid)); + + return dst_master + && (src_face.whatami != WhatAmI::Peer + || outface.whatami != WhatAmI::Peer + || tables.full_net(WhatAmI::Peer) + || tables.failover_brokering(src_face.zid, outface.zid)); + } + false +} + +#[cfg(feature = "stats")] +macro_rules! inc_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::buffer::Buffer; + match &$body { + PushBody::Put(p) => { + stats.[<$txrx _z_put_msgs>].[](1); + stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); + } + PushBody::Del(_) => { + stats.[<$txrx _z_del_msgs>].[](1); + } + } + } + } + }; +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn full_reentrant_route_data( + tables_ref: &RwLock, + face: &FaceState, + expr: &WireExpr, + ext_qos: ext::QoSType, + mut payload: PushBody, + routing_context: u64, +) { + let tables = zread!(tables_ref); + match tables.get_mapping(face, &expr.scope, expr.mapping).cloned() { + Some(prefix) => { + log::trace!( + "Route data for res {}{}", + prefix.expr(), + expr.suffix.as_ref() + ); + let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); + + #[cfg(feature = "stats")] + let admin = expr.full_expr().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, rx, user, payload) + } else { + inc_stats!(face, rx, admin, payload) + } + + if tables.whatami != WhatAmI::Router + || face.whatami != WhatAmI::Peer + || tables.peers_net.is_none() + || tables.zid + == *tables.elect_router(expr.full_expr(), tables.get_router_links(face.zid)) + { + let res = Resource::get_resource(&prefix, expr.suffix); + let route = get_data_route( + &tables, + face.whatami, + face.link_id, + &res, + &mut expr, + routing_context, + ); + let matching_pulls = get_matching_pulls(&tables, &res, &mut expr); + + if !(route.is_empty() && matching_pulls.is_empty()) { + treat_timestamp!(&tables.hlc, payload, tables.drop_future_timestamp); + + if route.len() == 1 && matching_pulls.len() == 0 { + let (outface, key_expr, context) = route.values().next().unwrap(); + if should_route(&tables, face, outface, &mut expr) { + drop(tables); + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr.into(), + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload, + }) + } + } else { + if !matching_pulls.is_empty() { + let lock = zlock!(tables.pull_caches_lock); + cache_data!(matching_pulls, expr, payload); + drop(lock); + } + + if tables.whatami == WhatAmI::Router { + let route = route + .values() + .filter(|(outface, _key_expr, _context)| { + should_route(&tables, face, outface, &mut expr) + }) + .cloned() + .collect::>(); + + drop(tables); + for (outface, key_expr, context) in route { + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr, + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload: payload.clone(), + }) + } + } else { + drop(tables); + for (outface, key_expr, context) in route.values() { + if face.id != outface.id + && match ( + face.mcast_group.as_ref(), + outface.mcast_group.as_ref(), + ) { + (Some(l), Some(r)) => l != r, + _ => true, + } + { + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr.into(), + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload: payload.clone(), + }) + } + } + } + } + } + } + } + None => { + log::error!("Route data with unknown scope {}!", expr.scope); + } + } +} + +pub(crate) fn pull_data(tables_ref: &RwLock, face: &Arc, expr: WireExpr) { + let tables = zread!(tables_ref); + match tables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + let res = get_mut_unchecked(&mut res); + match res.session_ctxs.get_mut(&face.id) { + Some(ctx) => match &ctx.subs { + Some(_subinfo) => { + // let reliability = subinfo.reliability; + let lock = zlock!(tables.pull_caches_lock); + let route = get_mut_unchecked(ctx) + .last_values + .drain() + .map(|(name, sample)| { + ( + Resource::get_best_key(&tables.root_res, &name, face.id) + .to_owned(), + sample, + ) + }) + .collect::>(); + drop(lock); + drop(tables); + for (key_expr, payload) in route { + face.primitives.send_push(Push { + wire_expr: key_expr, + ext_qos: ext::QoSType::push_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload, + }); + } + } + None => { + log::error!( + "Pull data for unknown subscription {} (no info)!", + prefix.expr() + expr.suffix.as_ref() + ); + } + }, + None => { + log::error!( + "Pull data for unknown subscription {} (no context)!", + prefix.expr() + expr.suffix.as_ref() + ); + } + } + } + None => { + log::error!( + "Pull data for unknown subscription {} (no resource)!", + prefix.expr() + expr.suffix.as_ref() + ); + } + }, + None => { + log::error!("Pull data with unknown scope {}!", expr.scope); + } + }; +} diff --git a/zenoh/src/net/routing/queries.rs b/zenoh/src/net/routing/queries.rs new file mode 100644 index 0000000000..25f062edd7 --- /dev/null +++ b/zenoh/src/net/routing/queries.rs @@ -0,0 +1,2352 @@ +// +// 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, +// +use super::face::FaceState; +use super::network::Network; +use super::resource::{ + QueryRoute, QueryRoutes, QueryTargetQabl, QueryTargetQablSet, Resource, RoutingContext, + SessionContext, +}; +use super::router::{RoutingExpr, Tables, TablesLock}; +use async_trait::async_trait; +use ordered_float::OrderedFloat; +use petgraph::graph::NodeIndex; +use std::borrow::Cow; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::sync::{Arc, RwLockReadGuard, Weak}; +use zenoh_buffers::ZBuf; +use zenoh_protocol::{ + core::{ + key_expr::{ + include::{Includer, DEFAULT_INCLUDER}, + keyexpr, OwnedKeyExpr, + }, + Encoding, WhatAmI, WireExpr, ZenohId, + }, + network::{ + declare::{ + common::ext::WireExprType, ext, queryable::ext::QueryableInfo, Declare, DeclareBody, + DeclareQueryable, UndeclareQueryable, + }, + request::{ext::TargetType, Request, RequestId}, + response::{self, ext::ResponderIdType, Response, ResponseFinal}, + }, + zenoh::{reply::ext::ConsolidationType, Reply, RequestBody, ResponseBody}, +}; +use zenoh_sync::get_mut_unchecked; +use zenoh_util::Timed; + +pub(crate) struct Query { + src_face: Arc, + src_qid: RequestId, +} + +#[cfg(feature = "complete_n")] +#[inline] +fn merge_qabl_infos(mut this: QueryableInfo, info: &QueryableInfo) -> QueryableInfo { + this.complete += info.complete; + this.distance = std::cmp::min(this.distance, info.distance); + this +} + +#[cfg(not(feature = "complete_n"))] +#[inline] +fn merge_qabl_infos(mut this: QueryableInfo, info: &QueryableInfo) -> QueryableInfo { + this.complete = u8::from(this.complete != 0 || info.complete != 0); + this.distance = std::cmp::min(this.distance, info.distance); + this +} + +fn local_router_qabl_info(tables: &Tables, res: &Arc) -> QueryableInfo { + let info = if tables.full_net(WhatAmI::Peer) { + res.context.as_ref().and_then(|ctx| { + ctx.peer_qabls.iter().fold(None, |accu, (zid, info)| { + if *zid != tables.zid { + Some(match accu { + Some(accu) => merge_qabl_infos(accu, info), + None => *info, + }) + } else { + accu + } + }) + }) + } else { + None + }; + res.session_ctxs + .values() + .fold(info, |accu, ctx| { + if let Some(info) = ctx.qabl.as_ref() { + Some(match accu { + Some(accu) => merge_qabl_infos(accu, info), + None => *info, + }) + } else { + accu + } + }) + .unwrap_or(QueryableInfo { + complete: 0, + distance: 0, + }) +} + +fn local_peer_qabl_info(tables: &Tables, res: &Arc) -> QueryableInfo { + let info = if tables.whatami == WhatAmI::Router && res.context.is_some() { + res.context() + .router_qabls + .iter() + .fold(None, |accu, (zid, info)| { + if *zid != tables.zid { + Some(match accu { + Some(accu) => merge_qabl_infos(accu, info), + None => *info, + }) + } else { + accu + } + }) + } else { + None + }; + res.session_ctxs + .values() + .fold(info, |accu, ctx| { + if let Some(info) = ctx.qabl.as_ref() { + Some(match accu { + Some(accu) => merge_qabl_infos(accu, info), + None => *info, + }) + } else { + accu + } + }) + .unwrap_or(QueryableInfo { + complete: 0, + distance: 0, + }) +} + +fn local_qabl_info(tables: &Tables, res: &Arc, face: &Arc) -> QueryableInfo { + let mut info = if tables.whatami == WhatAmI::Router && res.context.is_some() { + res.context() + .router_qabls + .iter() + .fold(None, |accu, (zid, info)| { + if *zid != tables.zid { + Some(match accu { + Some(accu) => merge_qabl_infos(accu, info), + None => *info, + }) + } else { + accu + } + }) + } else { + None + }; + if res.context.is_some() && tables.full_net(WhatAmI::Peer) { + info = res + .context() + .peer_qabls + .iter() + .fold(info, |accu, (zid, info)| { + if *zid != tables.zid { + Some(match accu { + Some(accu) => merge_qabl_infos(accu, info), + None => *info, + }) + } else { + accu + } + }) + } + res.session_ctxs + .values() + .fold(info, |accu, ctx| { + if ctx.face.id != face.id && ctx.face.whatami != WhatAmI::Peer + || face.whatami != WhatAmI::Peer + || tables.failover_brokering(ctx.face.zid, face.zid) + { + if let Some(info) = ctx.qabl.as_ref() { + Some(match accu { + Some(accu) => merge_qabl_infos(accu, info), + None => *info, + }) + } else { + accu + } + } else { + accu + } + }) + .unwrap_or(QueryableInfo { + complete: 0, + distance: 0, + }) +} + +#[allow(clippy::too_many_arguments)] +#[inline] +fn send_sourced_queryable_to_net_childs( + tables: &Tables, + net: &Network, + childs: &[NodeIndex], + res: &Arc, + qabl_info: &QueryableInfo, + src_face: Option<&mut Arc>, + routing_context: Option, +) { + for child in childs { + if net.graph.contains_node(*child) { + match tables.get_face(&net.graph[*child].zid).cloned() { + Some(mut someface) => { + if src_face.is_none() || someface.id != src_face.as_ref().unwrap().id { + let key_expr = Resource::decl_key(res, &mut someface); + + log::debug!("Send queryable {} on {}", res.expr(), someface); + + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: *qabl_info, + }), + }); + } + } + None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), + } + } + } +} + +fn propagate_simple_queryable( + tables: &mut Tables, + res: &Arc, + src_face: Option<&mut Arc>, +) { + let full_peers_net = tables.full_net(WhatAmI::Peer); + let faces = tables.faces.values().cloned(); + for mut dst_face in faces { + let info = local_qabl_info(tables, res, &dst_face); + let current_info = dst_face.local_qabls.get(res); + if (src_face.is_none() || src_face.as_ref().unwrap().id != dst_face.id) + && (current_info.is_none() || *current_info.unwrap() != info) + && match tables.whatami { + WhatAmI::Router => { + if full_peers_net { + dst_face.whatami == WhatAmI::Client + } else { + dst_face.whatami != WhatAmI::Router + && (src_face.is_none() + || src_face.as_ref().unwrap().whatami != WhatAmI::Peer + || dst_face.whatami != WhatAmI::Peer + || tables.failover_brokering( + src_face.as_ref().unwrap().zid, + dst_face.zid, + )) + } + } + WhatAmI::Peer => { + if full_peers_net { + dst_face.whatami == WhatAmI::Client + } else { + src_face.is_none() + || src_face.as_ref().unwrap().whatami == WhatAmI::Client + || dst_face.whatami == WhatAmI::Client + } + } + _ => { + src_face.is_none() + || src_face.as_ref().unwrap().whatami == WhatAmI::Client + || dst_face.whatami == WhatAmI::Client + } + } + { + get_mut_unchecked(&mut dst_face) + .local_qabls + .insert(res.clone(), info); + let key_expr = Resource::decl_key(res, &mut dst_face); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); + } + } +} + +fn propagate_sourced_queryable( + tables: &Tables, + res: &Arc, + qabl_info: &QueryableInfo, + src_face: Option<&mut Arc>, + source: &ZenohId, + net_type: WhatAmI, +) { + let net = tables.get_net(net_type).unwrap(); + match net.get_idx(source) { + Some(tree_sid) => { + if net.trees.len() > tree_sid.index() { + send_sourced_queryable_to_net_childs( + tables, + net, + &net.trees[tree_sid.index()].childs, + res, + qabl_info, + src_face, + Some(tree_sid.index() as u16), + ); + } else { + log::trace!( + "Propagating qabl {}: tree for node {} sid:{} not yet ready", + res.expr(), + tree_sid.index(), + source + ); + } + } + None => log::error!( + "Error propagating qabl {}: cannot get index of {}!", + res.expr(), + source + ), + } +} + +fn register_router_queryable( + tables: &mut Tables, + mut face: Option<&mut Arc>, + res: &mut Arc, + qabl_info: &QueryableInfo, + router: ZenohId, +) { + let current_info = res.context().router_qabls.get(&router); + if current_info.is_none() || current_info.unwrap() != qabl_info { + // Register router queryable + { + log::debug!( + "Register router queryable {} (router: {})", + res.expr(), + router, + ); + get_mut_unchecked(res) + .context_mut() + .router_qabls + .insert(router, *qabl_info); + tables.router_qabls.insert(res.clone()); + } + + // Propagate queryable to routers + propagate_sourced_queryable( + tables, + res, + qabl_info, + face.as_deref_mut(), + &router, + WhatAmI::Router, + ); + } + + if tables.full_net(WhatAmI::Peer) { + // Propagate queryable to peers + if face.is_none() || face.as_ref().unwrap().whatami != WhatAmI::Peer { + let local_info = local_peer_qabl_info(tables, res); + register_peer_queryable(tables, face.as_deref_mut(), res, &local_info, tables.zid) + } + } + + // Propagate queryable to clients + propagate_simple_queryable(tables, res, face); +} + +pub(crate) fn declare_router_queryable( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + qabl_info: &QueryableInfo, + router: ZenohId, +) { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { + Some(mut prefix) => { + let res = Resource::get_resource(&prefix, &expr.suffix); + let (mut res, mut wtables) = + if res.as_ref().map(|r| r.context.is_some()).unwrap_or(false) { + drop(rtables); + let wtables = zwrite!(tables.tables); + (res.unwrap(), wtables) + } else { + let mut fullexpr = prefix.expr(); + fullexpr.push_str(expr.suffix.as_ref()); + log::debug!("Register router queryable {}", fullexpr); + let mut matches = keyexpr::new(fullexpr.as_str()) + .map(|ke| Resource::get_matches(&rtables, ke)) + .unwrap_or_default(); + drop(rtables); + let mut wtables = zwrite!(tables.tables); + let mut res = + Resource::make_resource(&mut wtables, &mut prefix, expr.suffix.as_ref()); + matches.push(Arc::downgrade(&res)); + Resource::match_resource(&wtables, &mut res, matches); + (res, wtables) + }; + register_router_queryable(&mut wtables, Some(face), &mut res, qabl_info, router); + disable_matches_query_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_query_routes = compute_matches_query_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + drop(wtables); + } + None => log::error!("Declare router queryable for unknown scope {}!", expr.scope), + } +} + +fn register_peer_queryable( + tables: &mut Tables, + mut face: Option<&mut Arc>, + res: &mut Arc, + qabl_info: &QueryableInfo, + peer: ZenohId, +) { + let current_info = res.context().peer_qabls.get(&peer); + if current_info.is_none() || current_info.unwrap() != qabl_info { + // Register peer queryable + { + log::debug!("Register peer queryable {} (peer: {})", res.expr(), peer,); + get_mut_unchecked(res) + .context_mut() + .peer_qabls + .insert(peer, *qabl_info); + tables.peer_qabls.insert(res.clone()); + } + + // Propagate queryable to peers + propagate_sourced_queryable( + tables, + res, + qabl_info, + face.as_deref_mut(), + &peer, + WhatAmI::Peer, + ); + } + + if tables.whatami == WhatAmI::Peer { + // Propagate queryable to clients + propagate_simple_queryable(tables, res, face); + } +} + +pub(crate) fn declare_peer_queryable( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + qabl_info: &QueryableInfo, + peer: ZenohId, +) { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { + Some(mut prefix) => { + let res = Resource::get_resource(&prefix, &expr.suffix); + let (mut res, mut wtables) = + if res.as_ref().map(|r| r.context.is_some()).unwrap_or(false) { + drop(rtables); + let wtables = zwrite!(tables.tables); + (res.unwrap(), wtables) + } else { + let mut fullexpr = prefix.expr(); + fullexpr.push_str(expr.suffix.as_ref()); + log::debug!("Register peer queryable {}", fullexpr); + let mut matches = keyexpr::new(fullexpr.as_str()) + .map(|ke| Resource::get_matches(&rtables, ke)) + .unwrap_or_default(); + drop(rtables); + let mut wtables = zwrite!(tables.tables); + let mut res = + Resource::make_resource(&mut wtables, &mut prefix, expr.suffix.as_ref()); + matches.push(Arc::downgrade(&res)); + Resource::match_resource(&wtables, &mut res, matches); + (res, wtables) + }; + let mut face = Some(face); + register_peer_queryable(&mut wtables, face.as_deref_mut(), &mut res, qabl_info, peer); + if wtables.whatami == WhatAmI::Router { + let local_info = local_router_qabl_info(&wtables, &res); + let zid = wtables.zid; + register_router_queryable(&mut wtables, face, &mut res, &local_info, zid); + } + disable_matches_query_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_query_routes = compute_matches_query_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + drop(wtables); + } + None => log::error!("Declare router queryable for unknown scope {}!", expr.scope), + } +} + +fn register_client_queryable( + _tables: &mut Tables, + face: &mut Arc, + res: &mut Arc, + qabl_info: &QueryableInfo, +) { + // Register queryable + { + let res = get_mut_unchecked(res); + log::debug!("Register queryable {} (face: {})", res.expr(), face,); + get_mut_unchecked(res.session_ctxs.entry(face.id).or_insert_with(|| { + Arc::new(SessionContext { + face: face.clone(), + local_expr_id: None, + remote_expr_id: None, + subs: None, + qabl: None, + last_values: HashMap::new(), + }) + })) + .qabl = Some(*qabl_info); + } + get_mut_unchecked(face).remote_qabls.insert(res.clone()); +} + +pub(crate) fn declare_client_queryable( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + qabl_info: &QueryableInfo, +) { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { + Some(mut prefix) => { + let res = Resource::get_resource(&prefix, &expr.suffix); + let (mut res, mut wtables) = + if res.as_ref().map(|r| r.context.is_some()).unwrap_or(false) { + drop(rtables); + let wtables = zwrite!(tables.tables); + (res.unwrap(), wtables) + } else { + let mut fullexpr = prefix.expr(); + fullexpr.push_str(expr.suffix.as_ref()); + log::debug!("Register client queryable {}", fullexpr); + let mut matches = keyexpr::new(fullexpr.as_str()) + .map(|ke| Resource::get_matches(&rtables, ke)) + .unwrap_or_default(); + drop(rtables); + let mut wtables = zwrite!(tables.tables); + let mut res = + Resource::make_resource(&mut wtables, &mut prefix, expr.suffix.as_ref()); + matches.push(Arc::downgrade(&res)); + Resource::match_resource(&wtables, &mut res, matches); + (res, wtables) + }; + + register_client_queryable(&mut wtables, face, &mut res, qabl_info); + + match wtables.whatami { + WhatAmI::Router => { + let local_details = local_router_qabl_info(&wtables, &res); + let zid = wtables.zid; + register_router_queryable( + &mut wtables, + Some(face), + &mut res, + &local_details, + zid, + ); + } + WhatAmI::Peer => { + if wtables.full_net(WhatAmI::Peer) { + let local_details = local_peer_qabl_info(&wtables, &res); + let zid = wtables.zid; + register_peer_queryable( + &mut wtables, + Some(face), + &mut res, + &local_details, + zid, + ); + } else { + propagate_simple_queryable(&mut wtables, &res, Some(face)); + } + } + _ => { + propagate_simple_queryable(&mut wtables, &res, Some(face)); + } + } + disable_matches_query_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_query_routes = compute_matches_query_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + drop(wtables); + } + None => log::error!("Declare queryable for unknown scope {}!", expr.scope), + } +} + +#[inline] +fn remote_router_qabls(tables: &Tables, res: &Arc) -> bool { + res.context.is_some() + && res + .context() + .router_qabls + .keys() + .any(|router| router != &tables.zid) +} + +#[inline] +fn remote_peer_qabls(tables: &Tables, res: &Arc) -> bool { + res.context.is_some() + && res + .context() + .peer_qabls + .keys() + .any(|peer| peer != &tables.zid) +} + +#[inline] +fn client_qabls(res: &Arc) -> Vec> { + res.session_ctxs + .values() + .filter_map(|ctx| { + if ctx.qabl.is_some() { + Some(ctx.face.clone()) + } else { + None + } + }) + .collect() +} + +#[inline] +fn send_forget_sourced_queryable_to_net_childs( + tables: &Tables, + net: &Network, + childs: &[NodeIndex], + res: &Arc, + src_face: Option<&Arc>, + routing_context: Option, +) { + for child in childs { + if net.graph.contains_node(*child) { + match tables.get_face(&net.graph[*child].zid).cloned() { + Some(mut someface) => { + if src_face.is_none() || someface.id != src_face.unwrap().id { + let wire_expr = Resource::decl_key(res, &mut someface); + + log::debug!("Send forget queryable {} on {}", res.expr(), someface); + + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + } + } + None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), + } + } + } +} + +fn propagate_forget_simple_queryable(tables: &mut Tables, res: &mut Arc) { + for face in tables.faces.values_mut() { + if face.local_qabls.contains_key(res) { + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + + get_mut_unchecked(face).local_qabls.remove(res); + } + } +} + +fn propagate_forget_simple_queryable_to_peers(tables: &mut Tables, res: &mut Arc) { + if !tables.full_net(WhatAmI::Peer) + && res.context().router_qabls.len() == 1 + && res.context().router_qabls.contains_key(&tables.zid) + { + for mut face in tables + .faces + .values() + .cloned() + .collect::>>() + { + if face.whatami == WhatAmI::Peer + && face.local_qabls.contains_key(res) + && !res.session_ctxs.values().any(|s| { + face.zid != s.face.zid + && s.qabl.is_some() + && (s.face.whatami == WhatAmI::Client + || (s.face.whatami == WhatAmI::Peer + && tables.failover_brokering(s.face.zid, face.zid))) + }) + { + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + + get_mut_unchecked(&mut face).local_qabls.remove(res); + } + } + } +} + +fn propagate_forget_sourced_queryable( + tables: &mut Tables, + res: &mut Arc, + src_face: Option<&Arc>, + source: &ZenohId, + net_type: WhatAmI, +) { + let net = tables.get_net(net_type).unwrap(); + match net.get_idx(source) { + Some(tree_sid) => { + if net.trees.len() > tree_sid.index() { + send_forget_sourced_queryable_to_net_childs( + tables, + net, + &net.trees[tree_sid.index()].childs, + res, + src_face, + Some(tree_sid.index() as u16), + ); + } else { + log::trace!( + "Propagating forget qabl {}: tree for node {} sid:{} not yet ready", + res.expr(), + tree_sid.index(), + source + ); + } + } + None => log::error!( + "Error propagating forget qabl {}: cannot get index of {}!", + res.expr(), + source + ), + } +} + +fn unregister_router_queryable(tables: &mut Tables, res: &mut Arc, router: &ZenohId) { + log::debug!( + "Unregister router queryable {} (router: {})", + res.expr(), + router, + ); + get_mut_unchecked(res) + .context_mut() + .router_qabls + .remove(router); + + if res.context().router_qabls.is_empty() { + tables.router_qabls.retain(|qabl| !Arc::ptr_eq(qabl, res)); + + if tables.full_net(WhatAmI::Peer) { + undeclare_peer_queryable(tables, None, res, &tables.zid.clone()); + } + propagate_forget_simple_queryable(tables, res); + } + + propagate_forget_simple_queryable_to_peers(tables, res); +} + +fn undeclare_router_queryable( + tables: &mut Tables, + face: Option<&Arc>, + res: &mut Arc, + router: &ZenohId, +) { + if res.context().router_qabls.contains_key(router) { + unregister_router_queryable(tables, res, router); + propagate_forget_sourced_queryable(tables, res, face, router, WhatAmI::Router); + } +} + +pub(crate) fn forget_router_queryable( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + router: &ZenohId, +) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + drop(rtables); + let mut wtables = zwrite!(tables.tables); + undeclare_router_queryable(&mut wtables, Some(face), &mut res, router); + disable_matches_query_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_query_routes = compute_matches_query_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + Resource::clean(&mut res); + drop(wtables); + } + None => log::error!("Undeclare unknown router queryable!"), + }, + None => log::error!("Undeclare router queryable with unknown scope!"), + } +} + +fn unregister_peer_queryable(tables: &mut Tables, res: &mut Arc, peer: &ZenohId) { + log::debug!("Unregister peer queryable {} (peer: {})", res.expr(), peer,); + get_mut_unchecked(res).context_mut().peer_qabls.remove(peer); + + if res.context().peer_qabls.is_empty() { + tables.peer_qabls.retain(|qabl| !Arc::ptr_eq(qabl, res)); + + if tables.whatami == WhatAmI::Peer { + propagate_forget_simple_queryable(tables, res); + } + } +} + +fn undeclare_peer_queryable( + tables: &mut Tables, + face: Option<&Arc>, + res: &mut Arc, + peer: &ZenohId, +) { + if res.context().peer_qabls.contains_key(peer) { + unregister_peer_queryable(tables, res, peer); + propagate_forget_sourced_queryable(tables, res, face, peer, WhatAmI::Peer); + } +} + +pub(crate) fn forget_peer_queryable( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, + peer: &ZenohId, +) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + drop(rtables); + let mut wtables = zwrite!(tables.tables); + undeclare_peer_queryable(&mut wtables, Some(face), &mut res, peer); + + if wtables.whatami == WhatAmI::Router { + let client_qabls = res.session_ctxs.values().any(|ctx| ctx.qabl.is_some()); + let peer_qabls = remote_peer_qabls(&wtables, &res); + let zid = wtables.zid; + if !client_qabls && !peer_qabls { + undeclare_router_queryable(&mut wtables, None, &mut res, &zid); + } else { + let local_info = local_router_qabl_info(&wtables, &res); + register_router_queryable(&mut wtables, None, &mut res, &local_info, zid); + } + } + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_query_routes = compute_matches_query_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + Resource::clean(&mut res); + drop(wtables); + } + None => log::error!("Undeclare unknown peer queryable!"), + }, + None => log::error!("Undeclare peer queryable with unknown scope!"), + } +} + +pub(crate) fn undeclare_client_queryable( + tables: &mut Tables, + face: &mut Arc, + res: &mut Arc, +) { + log::debug!("Unregister client queryable {} for {}", res.expr(), face); + if let Some(ctx) = get_mut_unchecked(res).session_ctxs.get_mut(&face.id) { + get_mut_unchecked(ctx).qabl = None; + if ctx.qabl.is_none() { + get_mut_unchecked(face).remote_qabls.remove(res); + } + } + + let mut client_qabls = client_qabls(res); + let router_qabls = remote_router_qabls(tables, res); + let peer_qabls = remote_peer_qabls(tables, res); + + match tables.whatami { + WhatAmI::Router => { + if client_qabls.is_empty() && !peer_qabls { + undeclare_router_queryable(tables, None, res, &tables.zid.clone()); + } else { + let local_info = local_router_qabl_info(tables, res); + register_router_queryable(tables, None, res, &local_info, tables.zid); + propagate_forget_simple_queryable_to_peers(tables, res); + } + } + WhatAmI::Peer => { + if tables.full_net(WhatAmI::Peer) { + if client_qabls.is_empty() { + undeclare_peer_queryable(tables, None, res, &tables.zid.clone()); + } else { + let local_info = local_peer_qabl_info(tables, res); + register_peer_queryable(tables, None, res, &local_info, tables.zid); + } + } else if client_qabls.is_empty() { + propagate_forget_simple_queryable(tables, res); + } else { + propagate_simple_queryable(tables, res, None); + } + } + _ => { + if client_qabls.is_empty() { + propagate_forget_simple_queryable(tables, res); + } else { + propagate_simple_queryable(tables, res, None); + } + } + } + + if client_qabls.len() == 1 && !router_qabls && !peer_qabls { + let face = &mut client_qabls[0]; + if face.local_qabls.contains_key(res) { + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + + get_mut_unchecked(face).local_qabls.remove(res); + } + } +} + +pub(crate) fn forget_client_queryable( + tables: &TablesLock, + rtables: RwLockReadGuard, + face: &mut Arc, + expr: &WireExpr, +) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + drop(rtables); + let mut wtables = zwrite!(tables.tables); + undeclare_client_queryable(&mut wtables, face, &mut res); + disable_matches_query_routes(&mut wtables, &mut res); + drop(wtables); + + let rtables = zread!(tables.tables); + let matches_query_routes = compute_matches_query_routes_(&rtables, &res); + drop(rtables); + + let wtables = zwrite!(tables.tables); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + Resource::clean(&mut res); + drop(wtables); + } + None => log::error!("Undeclare unknown queryable!"), + }, + None => log::error!("Undeclare queryable with unknown scope!"), + } +} + +pub(crate) fn queries_new_face(tables: &mut Tables, face: &mut Arc) { + match tables.whatami { + WhatAmI::Router => { + if face.whatami == WhatAmI::Client { + for qabl in tables.router_qabls.iter() { + if qabl.context.is_some() { + let info = local_qabl_info(tables, qabl, face); + get_mut_unchecked(face) + .local_qabls + .insert(qabl.clone(), info); + let key_expr = Resource::decl_key(qabl, face); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); + } + } + } else if face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + for qabl in tables.router_qabls.iter() { + if qabl.context.is_some() + && (qabl.context().router_qabls.keys().any(|r| *r != tables.zid) + || qabl.session_ctxs.values().any(|s| { + s.qabl.is_some() + && (s.face.whatami == WhatAmI::Client + || (s.face.whatami == WhatAmI::Peer + && tables.failover_brokering(s.face.zid, face.zid))) + })) + { + let info = local_qabl_info(tables, qabl, face); + get_mut_unchecked(face) + .local_qabls + .insert(qabl.clone(), info); + let key_expr = Resource::decl_key(qabl, face); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); + } + } + } + } + WhatAmI::Peer => { + if tables.full_net(WhatAmI::Peer) { + if face.whatami == WhatAmI::Client { + for qabl in &tables.peer_qabls { + if qabl.context.is_some() { + let info = local_qabl_info(tables, qabl, face); + get_mut_unchecked(face) + .local_qabls + .insert(qabl.clone(), info); + let key_expr = Resource::decl_key(qabl, face); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); + } + } + } + } else { + for face in tables + .faces + .values() + .cloned() + .collect::>>() + { + for qabl in face.remote_qabls.iter() { + propagate_simple_queryable(tables, qabl, Some(&mut face.clone())); + } + } + } + } + WhatAmI::Client => { + for face in tables + .faces + .values() + .cloned() + .collect::>>() + { + for qabl in face.remote_qabls.iter() { + propagate_simple_queryable(tables, qabl, Some(&mut face.clone())); + } + } + } + } +} + +pub(crate) fn queries_remove_node(tables: &mut Tables, node: &ZenohId, net_type: WhatAmI) { + match net_type { + WhatAmI::Router => { + let mut qabls = vec![]; + for res in tables.router_qabls.iter() { + for qabl in res.context().router_qabls.keys() { + if qabl == node { + qabls.push(res.clone()); + } + } + } + for mut res in qabls { + unregister_router_queryable(tables, &mut res, node); + + let matches_query_routes = compute_matches_query_routes_(tables, &res); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + Resource::clean(&mut res); + } + } + WhatAmI::Peer => { + let mut qabls = vec![]; + for res in tables.router_qabls.iter() { + for qabl in res.context().router_qabls.keys() { + if qabl == node { + qabls.push(res.clone()); + } + } + } + for mut res in qabls { + unregister_peer_queryable(tables, &mut res, node); + + if tables.whatami == WhatAmI::Router { + let client_qabls = res.session_ctxs.values().any(|ctx| ctx.qabl.is_some()); + let peer_qabls = remote_peer_qabls(tables, &res); + if !client_qabls && !peer_qabls { + undeclare_router_queryable(tables, None, &mut res, &tables.zid.clone()); + } else { + let local_info = local_router_qabl_info(tables, &res); + register_router_queryable(tables, None, &mut res, &local_info, tables.zid); + } + } + + let matches_query_routes = compute_matches_query_routes_(tables, &res); + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + } + Resource::clean(&mut res) + } + } + _ => (), + } +} + +pub(crate) fn queries_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: &[ZenohId]) { + if let Some(src_face) = tables.get_face(zid) { + if tables.router_peers_failover_brokering + && tables.whatami == WhatAmI::Router + && src_face.whatami == WhatAmI::Peer + { + for res in &src_face.remote_qabls { + let client_qabls = res + .session_ctxs + .values() + .any(|ctx| ctx.face.whatami == WhatAmI::Client && ctx.qabl.is_some()); + if !remote_router_qabls(tables, res) && !client_qabls { + for ctx in get_mut_unchecked(&mut res.clone()) + .session_ctxs + .values_mut() + { + let dst_face = &mut get_mut_unchecked(ctx).face; + if dst_face.whatami == WhatAmI::Peer && src_face.zid != dst_face.zid { + if dst_face.local_qabls.contains_key(res) { + let forget = !Tables::failover_brokering_to(links, dst_face.zid) + && { + let ctx_links = tables + .peers_net + .as_ref() + .map(|net| net.get_links(dst_face.zid)) + .unwrap_or_else(|| &[]); + res.session_ctxs.values().any(|ctx2| { + ctx2.face.whatami == WhatAmI::Peer + && ctx2.qabl.is_some() + && Tables::failover_brokering_to( + ctx_links, + ctx2.face.zid, + ) + }) + }; + if forget { + let wire_expr = Resource::get_best_key(res, "", dst_face.id); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); + + get_mut_unchecked(dst_face).local_qabls.remove(res); + } + } else if Tables::failover_brokering_to(links, ctx.face.zid) { + let dst_face = &mut get_mut_unchecked(ctx).face; + let info = local_qabl_info(tables, res, dst_face); + get_mut_unchecked(dst_face) + .local_qabls + .insert(res.clone(), info); + let key_expr = Resource::decl_key(res, dst_face); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); + } + } + } + } + } + } + } +} + +pub(crate) fn queries_tree_change( + tables: &mut Tables, + new_childs: &[Vec], + net_type: WhatAmI, +) { + // propagate qabls to new childs + for (tree_sid, tree_childs) in new_childs.iter().enumerate() { + if !tree_childs.is_empty() { + let net = tables.get_net(net_type).unwrap(); + let tree_idx = NodeIndex::new(tree_sid); + if net.graph.contains_node(tree_idx) { + let tree_id = net.graph[tree_idx].zid; + + let qabls_res = match net_type { + WhatAmI::Router => &tables.router_qabls, + _ => &tables.peer_qabls, + }; + + for res in qabls_res { + let qabls = match net_type { + WhatAmI::Router => &res.context().router_qabls, + _ => &res.context().peer_qabls, + }; + if let Some(qabl_info) = qabls.get(&tree_id) { + send_sourced_queryable_to_net_childs( + tables, + net, + tree_childs, + res, + qabl_info, + None, + Some(tree_sid as u16), + ); + } + } + } + } + } + + // recompute routes + compute_query_routes_from(tables, &mut tables.root_res.clone()); +} + +#[inline] +#[allow(clippy::too_many_arguments)] +fn insert_target_for_qabls( + route: &mut QueryTargetQablSet, + expr: &mut RoutingExpr, + tables: &Tables, + net: &Network, + source: usize, + qabls: &HashMap, + complete: bool, +) { + if net.trees.len() > source { + for (qabl, qabl_info) in qabls { + if let Some(qabl_idx) = net.get_idx(qabl) { + if net.trees[source].directions.len() > qabl_idx.index() { + if let Some(direction) = net.trees[source].directions[qabl_idx.index()] { + if net.graph.contains_node(direction) { + if let Some(face) = tables.get_face(&net.graph[direction].zid) { + if net.distances.len() > qabl_idx.index() { + let key_expr = + Resource::get_best_key(expr.prefix, expr.suffix, face.id); + route.push(QueryTargetQabl { + direction: ( + face.clone(), + key_expr.to_owned(), + if source != 0 { + Some(source as u16) + } else { + None + }, + ), + complete: if complete { + qabl_info.complete as u64 + } else { + 0 + }, + distance: net.distances[qabl_idx.index()], + }); + } + } + } + } + } + } + } + } else { + log::trace!("Tree for node sid:{} not yet ready", source); + } +} + +lazy_static::lazy_static! { + static ref EMPTY_ROUTE: Arc = Arc::new(Vec::new()); +} +fn compute_query_route( + tables: &Tables, + expr: &mut RoutingExpr, + source: Option, + source_type: WhatAmI, +) -> Arc { + let mut route = QueryTargetQablSet::new(); + let key_expr = expr.full_expr(); + if key_expr.ends_with('/') { + return EMPTY_ROUTE.clone(); + } + log::trace!( + "compute_query_route({}, {:?}, {:?})", + key_expr, + source, + source_type + ); + let key_expr = match OwnedKeyExpr::try_from(key_expr) { + Ok(ke) => ke, + Err(e) => { + log::warn!("Invalid KE reached the system: {}", e); + return EMPTY_ROUTE.clone(); + } + }; + let res = Resource::get_resource(expr.prefix, expr.suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); + + let master = tables.whatami != WhatAmI::Router + || !tables.full_net(WhatAmI::Peer) + || *tables.elect_router(&key_expr, tables.shared_nodes.iter()) == tables.zid; + + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + let complete = DEFAULT_INCLUDER.includes(mres.expr().as_bytes(), key_expr.as_bytes()); + if tables.whatami == WhatAmI::Router { + if master || source_type == WhatAmI::Router { + let net = tables.routers_net.as_ref().unwrap(); + let router_source = match source_type { + WhatAmI::Router => source.unwrap(), + _ => net.idx.index(), + }; + insert_target_for_qabls( + &mut route, + expr, + tables, + net, + router_source, + &mres.context().router_qabls, + complete, + ); + } + + if (master || source_type != WhatAmI::Router) && tables.full_net(WhatAmI::Peer) { + let net = tables.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_target_for_qabls( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_qabls, + complete, + ); + } + } + + if tables.whatami == WhatAmI::Peer && tables.full_net(WhatAmI::Peer) { + let net = tables.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Router | WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_target_for_qabls( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_qabls, + complete, + ); + } + + if tables.whatami != WhatAmI::Router || master || source_type == WhatAmI::Router { + for (sid, context) in &mres.session_ctxs { + if match tables.whatami { + WhatAmI::Router => context.face.whatami != WhatAmI::Router, + _ => source_type == WhatAmI::Client || context.face.whatami == WhatAmI::Client, + } { + let key_expr = Resource::get_best_key(expr.prefix, expr.suffix, *sid); + if let Some(qabl_info) = context.qabl.as_ref() { + route.push(QueryTargetQabl { + direction: (context.face.clone(), key_expr.to_owned(), None), + complete: if complete { + qabl_info.complete as u64 + } else { + 0 + }, + distance: 0.5, + }); + } + } + } + } + } + route.sort_by_key(|qabl| OrderedFloat(qabl.distance)); + Arc::new(route) +} + +pub(super) fn compute_query_routes_(tables: &Tables, res: &Arc) -> QueryRoutes { + let mut routes = QueryRoutes { + routers_query_routes: vec![], + peers_query_routes: vec![], + peer_query_route: None, + client_query_route: None, + }; + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .routers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + routes.routers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + if !tables.full_net(WhatAmI::Peer) { + routes.peer_query_route = + Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.full_net(WhatAmI::Peer) + { + let indexes = tables + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .peers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + routes.peers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + routes.client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + routes.peer_query_route = Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + routes.client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + } + routes +} + +pub(crate) fn compute_query_routes(tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + let mut res_mut = res.clone(); + let res_mut = get_mut_unchecked(&mut res_mut); + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let routers_query_routes = &mut res_mut.context_mut().routers_query_routes; + routers_query_routes.clear(); + routers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + routers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + if !tables.full_net(WhatAmI::Peer) { + res_mut.context_mut().peer_query_route = + Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.full_net(WhatAmI::Peer) + { + let indexes = tables + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let peers_query_routes = &mut res_mut.context_mut().peers_query_routes; + peers_query_routes.clear(); + peers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + peers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + res_mut.context_mut().client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + res_mut.context_mut().peer_query_route = + Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + res_mut.context_mut().client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + } + } +} + +fn compute_query_routes_from(tables: &mut Tables, res: &mut Arc) { + compute_query_routes(tables, res); + let res = get_mut_unchecked(res); + for child in res.childs.values_mut() { + compute_query_routes_from(tables, child); + } +} + +pub(super) fn compute_matches_query_routes_( + tables: &Tables, + res: &Arc, +) -> Vec<(Arc, QueryRoutes)> { + let mut routes = vec![]; + if res.context.is_some() { + routes.push((res.clone(), compute_query_routes_(tables, res))); + for match_ in &res.context().matches { + let match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + let match_routes = compute_query_routes_(tables, &match_); + routes.push((match_, match_routes)); + } + } + } + routes +} + +#[inline] +fn insert_pending_query(outface: &mut Arc, query: Arc) -> RequestId { + let outface_mut = get_mut_unchecked(outface); + outface_mut.next_qid += 1; + let qid = outface_mut.next_qid; + outface_mut.pending_queries.insert(qid, query); + qid +} + +#[inline] +fn should_route( + tables: &Tables, + src_face: &FaceState, + outface: &Arc, + expr: &mut RoutingExpr, +) -> bool { + if src_face.id != outface.id { + let dst_master = tables.whatami != WhatAmI::Router + || outface.whatami != WhatAmI::Peer + || tables.peers_net.is_none() + || tables.zid + == *tables.elect_router(expr.full_expr(), tables.get_router_links(outface.zid)); + + return dst_master + && (src_face.whatami != WhatAmI::Peer + || outface.whatami != WhatAmI::Peer + || tables.full_net(WhatAmI::Peer) + || tables.failover_brokering(src_face.zid, outface.zid)); + } + false +} + +#[inline] +fn compute_final_route( + tables: &Tables, + qabls: &Arc, + src_face: &Arc, + expr: &mut RoutingExpr, + target: &TargetType, + query: Arc, +) -> QueryRoute { + match target { + TargetType::All => { + let mut route = HashMap::new(); + for qabl in qabls.iter() { + if should_route(tables, src_face, &qabl.direction.0, expr) { + #[cfg(feature = "complete_n")] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, *target) + }); + } + #[cfg(not(feature = "complete_n"))] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid) + }); + } + } + } + route + } + TargetType::AllComplete => { + let mut route = HashMap::new(); + for qabl in qabls.iter() { + if qabl.complete > 0 && should_route(tables, src_face, &qabl.direction.0, expr) { + #[cfg(feature = "complete_n")] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, *target) + }); + } + #[cfg(not(feature = "complete_n"))] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid) + }); + } + } + } + route + } + #[cfg(feature = "complete_n")] + TargetType::Complete(n) => { + let mut route = HashMap::new(); + let mut remaining = *n; + if src_face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + let source_links = tables + .peers_net + .as_ref() + .map(|net| net.get_links(src_face.zid)) + .unwrap_or_default(); + for qabl in qabls.iter() { + if qabl.direction.0.id != src_face.id + && qabl.complete > 0 + && (qabl.direction.0.whatami != WhatAmI::Peer + || (tables.router_peers_failover_brokering + && Tables::failover_brokering_to( + source_links, + qabl.direction.0.zid, + ))) + { + let nb = std::cmp::min(qabl.complete, remaining); + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, TargetType::Complete(nb)) + }); + remaining -= nb; + if remaining == 0 { + break; + } + } + } + } else { + for qabl in qabls.iter() { + if qabl.direction.0.id != src_face.id && qabl.complete > 0 { + let nb = std::cmp::min(qabl.complete, remaining); + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, TargetType::Complete(nb)) + }); + remaining -= nb; + if remaining == 0 { + break; + } + } + } + } + route + } + TargetType::BestMatching => { + if let Some(qabl) = qabls + .iter() + .find(|qabl| qabl.direction.0.id != src_face.id && qabl.complete > 0) + { + let mut route = HashMap::new(); + #[cfg(feature = "complete_n")] + { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query); + route.insert(direction.0.id, (direction, qid, *target)); + } + #[cfg(not(feature = "complete_n"))] + { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query); + route.insert(direction.0.id, (direction, qid)); + } + route + } else { + compute_final_route(tables, qabls, src_face, expr, &TargetType::All, query) + } + } + } +} + +#[inline] +fn compute_local_replies( + tables: &Tables, + prefix: &Arc, + suffix: &str, + face: &Arc, +) -> Vec<(WireExpr<'static>, ZBuf)> { + let mut result = vec![]; + // Only the first routing point in the query route + // should return the liveliness tokens + if face.whatami == WhatAmI::Client { + let key_expr = prefix.expr() + suffix; + let key_expr = match OwnedKeyExpr::try_from(key_expr) { + Ok(ke) => ke, + Err(e) => { + log::warn!("Invalid KE reached the system: {}", e); + return result; + } + }; + if key_expr.starts_with(super::PREFIX_LIVELINESS) { + let res = Resource::get_resource(prefix, suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + if (mres.context.is_some() + && (!mres.context().router_subs.is_empty() + || !mres.context().peer_subs.is_empty())) + || mres.session_ctxs.values().any(|ctx| ctx.subs.is_some()) + { + result.push((Resource::get_best_key(&mres, "", face.id), ZBuf::default())); + } + } + } + } + result +} + +#[derive(Clone)] +struct QueryCleanup { + tables: Arc, + face: Weak, + qid: RequestId, +} + +#[async_trait] +impl Timed for QueryCleanup { + async fn run(&mut self) { + if let Some(mut face) = self.face.upgrade() { + let tables_lock = zwrite!(self.tables.tables); + if let Some(query) = get_mut_unchecked(&mut face) + .pending_queries + .remove(&self.qid) + { + drop(tables_lock); + log::warn!( + "Didn't receive final reply {}:{} from {}: Timeout!", + query.src_face, + self.qid, + face + ); + finalize_pending_query(query); + } + } + } +} + +pub(super) fn disable_matches_query_routes(_tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + get_mut_unchecked(res).context_mut().valid_query_routes = false; + for match_ in &res.context().matches { + let mut match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + get_mut_unchecked(&mut match_) + .context_mut() + .valid_query_routes = false; + } + } + } +} + +#[inline] +fn get_query_route( + tables: &Tables, + face: &FaceState, + res: &Option>, + expr: &mut RoutingExpr, + routing_context: u64, +) -> Arc { + match tables.whatami { + WhatAmI::Router => match face.whatami { + WhatAmI::Router => { + let routers_net = tables.routers_net.as_ref().unwrap(); + let local_context = routers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.routers_query_route(local_context)) + .unwrap_or_else(|| { + compute_query_route(tables, expr, Some(local_context), face.whatami) + }) + } + WhatAmI::Peer => { + if tables.full_net(WhatAmI::Peer) { + let peers_net = tables.peers_net.as_ref().unwrap(); + let local_context = peers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.peers_query_route(local_context)) + .unwrap_or_else(|| { + compute_query_route(tables, expr, Some(local_context), face.whatami) + }) + } else { + res.as_ref() + .and_then(|res| res.peer_query_route()) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.routers_query_route(0)) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), + }, + WhatAmI::Peer => { + if tables.full_net(WhatAmI::Peer) { + match face.whatami { + WhatAmI::Router | WhatAmI::Peer => { + let peers_net = tables.peers_net.as_ref().unwrap(); + let local_context = + peers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.peers_query_route(local_context)) + .unwrap_or_else(|| { + compute_query_route(tables, expr, Some(local_context), face.whatami) + }) + } + _ => res + .as_ref() + .and_then(|res| res.peers_query_route(0)) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), + } + } else { + res.as_ref() + .and_then(|res| match face.whatami { + WhatAmI::Client => res.client_query_route(), + _ => res.peer_query_route(), + }) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.client_query_route()) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), + } +} + +#[cfg(feature = "stats")] +macro_rules! inc_req_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::buffer::Buffer; + match &$body { + RequestBody::Put(p) => { + stats.[<$txrx _z_put_msgs>].[](1); + stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); + } + RequestBody::Del(_) => { + stats.[<$txrx _z_del_msgs>].[](1); + } + RequestBody::Query(q) => { + stats.[<$txrx _z_query_msgs>].[](1); + stats.[<$txrx _z_query_pl_bytes>].[]( + q.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), + ); + } + RequestBody::Pull(_) => (), + } + } + } + }; +} + +#[cfg(feature = "stats")] +macro_rules! inc_res_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::buffer::Buffer; + match &$body { + ResponseBody::Put(p) => { + stats.[<$txrx _z_put_msgs>].[](1); + stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); + } + ResponseBody::Reply(r) => { + stats.[<$txrx _z_reply_msgs>].[](1); + stats.[<$txrx _z_reply_pl_bytes>].[](r.payload.len()); + } + ResponseBody::Err(e) => { + stats.[<$txrx _z_reply_msgs>].[](1); + stats.[<$txrx _z_reply_pl_bytes>].[]( + e.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), + ); + } + ResponseBody::Ack(_) => (), + } + } + } + }; +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn route_query( + tables_ref: &Arc, + face: &Arc, + expr: &WireExpr, + qid: RequestId, + target: TargetType, + body: RequestBody, + routing_context: u64, +) { + let rtables = zread!(tables_ref.tables); + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => { + log::debug!( + "Route query {}:{} for res {}{}", + face, + qid, + prefix.expr(), + expr.suffix.as_ref(), + ); + let prefix = prefix.clone(); + let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); + + #[cfg(feature = "stats")] + let admin = expr.full_expr().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(face, rx, user, body) + } else { + inc_req_stats!(face, rx, admin, body) + } + + if rtables.whatami != WhatAmI::Router + || face.whatami != WhatAmI::Peer + || rtables.peers_net.is_none() + || rtables.zid + == *rtables.elect_router(expr.full_expr(), rtables.get_router_links(face.zid)) + { + let res = Resource::get_resource(&prefix, expr.suffix); + let route = get_query_route(&rtables, face, &res, &mut expr, routing_context); + + let query = Arc::new(Query { + src_face: face.clone(), + src_qid: qid, + }); + + let queries_lock = zwrite!(tables_ref.queries_lock); + let route = compute_final_route(&rtables, &route, face, &mut expr, &target, query); + let local_replies = compute_local_replies(&rtables, &prefix, expr.suffix, face); + let zid = rtables.zid; + + drop(queries_lock); + drop(rtables); + + for (expr, payload) in local_replies { + let payload = ResponseBody::Reply(Reply { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + ext_consolidation: ConsolidationType::default(), + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_attachment: None, // @TODO: expose it in the API + ext_unknown: vec![], + payload, + }); + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(face, tx, user, payload) + } else { + inc_res_stats!(face, tx, admin, payload) + } + + face.primitives.clone().send_response(Response { + rid: qid, + wire_expr: expr, + payload, + ext_qos: response::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_respid: Some(response::ext::ResponderIdType { + zid, + eid: 0, // TODO + }), + }); + } + + if route.is_empty() { + log::debug!( + "Send final reply {}:{} (no matching queryables or not master)", + face, + qid + ); + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } else { + // let timer = tables.timer.clone(); + // let timeout = tables.queries_default_timeout; + #[cfg(feature = "complete_n")] + { + for ((outface, key_expr, context), qid, t) in route.values() { + // timer.add(TimedEvent::once( + // Instant::now() + timeout, + // QueryCleanup { + // tables: tables_ref.clone(), + // face: Arc::downgrade(&outface), + // *qid, + // }, + // )); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(outface, tx, user, body) + } else { + inc_req_stats!(outface, tx, admin, body) + } + + log::trace!("Propagate query {}:{} to {}", face, qid, outface); + outface.primitives.send_request(Request { + id: *qid, + wire_expr: key_expr.into(), + ext_qos: ext::QoSType::request_default(), // TODO + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + ext_target: *t, + ext_budget: None, + ext_timeout: None, + payload: body.clone(), + }); + } + } + + #[cfg(not(feature = "complete_n"))] + { + for ((outface, key_expr, context), qid) in route.values() { + // timer.add(TimedEvent::once( + // Instant::now() + timeout, + // QueryCleanup { + // tables: tables_ref.clone(), + // face: Arc::downgrade(&outface), + // *qid, + // }, + // )); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(outface, tx, user, body) + } else { + inc_req_stats!(outface, tx, admin, body) + } + + log::trace!("Propagate query {}:{} to {}", face, qid, outface); + outface.primitives.send_request(Request { + id: *qid, + wire_expr: key_expr.into(), + ext_qos: ext::QoSType::request_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + ext_target: target, + ext_budget: None, + ext_timeout: None, + payload: body.clone(), + }); + } + } + } + } else { + log::debug!("Send final reply {}:{} (not master)", face, qid); + drop(rtables); + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } + } + None => { + log::error!( + "Route query with unknown scope {}! Send final reply.", + expr.scope + ); + drop(rtables); + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn route_send_response( + tables_ref: &Arc, + face: &mut Arc, + qid: RequestId, + ext_respid: Option, + key_expr: WireExpr, + body: ResponseBody, +) { + let queries_lock = zread!(tables_ref.queries_lock); + #[cfg(feature = "stats")] + let admin = key_expr.as_str().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(face, rx, user, body) + } else { + inc_res_stats!(face, rx, admin, body) + } + + match face.pending_queries.get(&qid) { + Some(query) => { + drop(queries_lock); + + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(query.src_face, tx, user, body) + } else { + inc_res_stats!(query.src_face, tx, admin, body) + } + + query.src_face.primitives.clone().send_response(Response { + rid: query.src_qid, + wire_expr: key_expr.to_owned(), + payload: body, + ext_qos: response::ext::QoSType::response_default(), + ext_tstamp: None, + ext_respid, + }); + } + None => log::warn!( + "Route reply {}:{} from {}: Query nof found!", + face, + qid, + face + ), + } +} + +pub(crate) fn route_send_response_final( + tables_ref: &Arc, + face: &mut Arc, + qid: RequestId, +) { + let queries_lock = zwrite!(tables_ref.queries_lock); + match get_mut_unchecked(face).pending_queries.remove(&qid) { + Some(query) => { + drop(queries_lock); + log::debug!( + "Received final reply {}:{} from {}", + query.src_face, + qid, + face + ); + finalize_pending_query(query); + } + None => log::warn!( + "Route final reply {}:{} from {}: Query nof found!", + face, + qid, + face + ), + } +} + +pub(crate) fn finalize_pending_queries(tables_ref: &TablesLock, face: &mut Arc) { + let queries_lock = zwrite!(tables_ref.queries_lock); + for (_, query) in get_mut_unchecked(face).pending_queries.drain() { + finalize_pending_query(query); + } + drop(queries_lock); +} + +pub(crate) fn finalize_pending_query(query: Arc) { + if let Some(query) = Arc::into_inner(query) { + log::debug!("Propagate final reply {}:{}", query.src_face, query.src_qid); + query + .src_face + .primitives + .clone() + .send_response_final(ResponseFinal { + rid: query.src_qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } +} diff --git a/zenoh/src/net/runtime/mod.rs b/zenoh/src/net/runtime/mod.rs index ac125421f6..61e36aafb7 100644 --- a/zenoh/src/net/runtime/mod.rs +++ b/zenoh/src/net/runtime/mod.rs @@ -17,8 +17,11 @@ //! This module is intended for Zenoh's internal use. //! //! [Click here for Zenoh's documentation](../zenoh/index.html) + +// ignore_tagging mod adminspace; -pub mod orchestrator; +// ignore_tagging +pub(crate) mod orchestrator; use super::primitives::DeMux; use super::routing; @@ -58,6 +61,7 @@ struct RuntimeState { stop_source: std::sync::RwLock>, } +// ignore_tagging #[derive(Clone)] pub struct Runtime { state: Arc, @@ -75,6 +79,7 @@ impl StructVersion for Runtime { impl PluginStartArgs for Runtime {} impl Runtime { + // ignore_tagging pub async fn new(config: Config) -> ZResult { let mut runtime = Runtime::init(config).await?; match runtime.start().await { @@ -146,14 +151,17 @@ impl Runtime { } #[inline(always)] + // ignore_tagging pub fn manager(&self) -> &TransportManager { &self.state.manager } + // ignore_tagging pub fn new_handler(&self, handler: Arc) { zwrite!(self.state.transport_handlers).push(handler); } + // ignore_tagging pub async fn close(&self) -> ZResult<()> { log::trace!("Runtime::close())"); drop(self.state.stop_source.write().unwrap().take()); @@ -161,10 +169,12 @@ impl Runtime { Ok(()) } + // ignore_tagging pub fn new_timestamp(&self) -> Option { self.state.hlc.as_ref().map(|hlc| hlc.new_timestamp()) } + // ignore_tagging pub fn get_locators(&self) -> Vec { self.state.locators.read().unwrap().clone() } @@ -186,18 +196,22 @@ impl Runtime { self.state.router.clone() } + // ignore_tagging pub fn config(&self) -> &Notifier { &self.state.config } + // ignore_tagging pub fn hlc(&self) -> Option<&HLC> { self.state.hlc.as_ref().map(Arc::as_ref) } + // ignore_tagging pub fn zid(&self) -> ZenohId { self.state.zid } + // ignore_tagging pub fn whatami(&self) -> WhatAmI { self.state.whatami } diff --git a/zenoh/src/net/tests/mod.rs b/zenoh/src/net/tests/mod.rs index b8b42bef12..43eba636a1 100644 --- a/zenoh/src/net/tests/mod.rs +++ b/zenoh/src/net/tests/mod.rs @@ -1 +1,2 @@ +// ignore_tagging pub(crate) mod tables; diff --git a/zenoh/src/net/tests/tables.rs b/zenoh/src/net/tests/tables.rs index 363803f682..c12f90ba99 100644 --- a/zenoh/src/net/tests/tables.rs +++ b/zenoh/src/net/tests/tables.rs @@ -365,7 +365,7 @@ fn clean_test() { assert!(res3.upgrade().is_none()); } -pub struct ClientPrimitives { +pub(crate) struct ClientPrimitives { data: std::sync::Mutex>>, mapping: std::sync::Mutex>, } diff --git a/zenoh/src/plugins/mod.rs b/zenoh/src/plugins/mod.rs index d72139cc29..fbd0066235 100644 --- a/zenoh/src/plugins/mod.rs +++ b/zenoh/src/plugins/mod.rs @@ -17,6 +17,7 @@ //! This module is intended for Zenoh's internal use. //! //! [Click here for Zenoh's documentation](../../zenoh/index.html) +// ignore_tagging pub(crate) mod sealed; #[zenoh_macros::unstable] diff --git a/zenoh/src/publication.rs b/zenoh/src/publication.rs index 843190ad45..d6949133f0 100644 --- a/zenoh/src/publication.rs +++ b/zenoh/src/publication.rs @@ -74,6 +74,7 @@ pub type DeleteBuilder<'a, 'b> = PutBuilder<'a, 'b>; /// .unwrap(); /// # }) /// ``` +/// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug, Clone)] pub struct PutBuilder<'a, 'b> { @@ -86,6 +87,7 @@ pub struct PutBuilder<'a, 'b> { impl PutBuilder<'_, '_> { /// Change the encoding of the written data. + // tags{session.put.encoding.set} #[inline] pub fn encoding(mut self, encoding: IntoEncoding) -> Self where @@ -95,6 +97,7 @@ impl PutBuilder<'_, '_> { self } /// Change the `congestion_control` to apply when routing the data. + // tags{session.put.congestion_control.set} #[inline] pub fn congestion_control(mut self, congestion_control: CongestionControl) -> Self { self.publisher = self.publisher.congestion_control(congestion_control); @@ -102,6 +105,7 @@ impl PutBuilder<'_, '_> { } /// Change the priority of the written data. + // tags{session.put.priority.set} #[inline] pub fn priority(mut self, priority: Priority) -> Self { self.publisher = self.publisher.priority(priority); @@ -110,6 +114,7 @@ impl PutBuilder<'_, '_> { /// Restrict the matching subscribers that will receive the published data /// to the ones that have the given [`Locality`](crate::prelude::Locality). + // tags{session.put.allowed_destination.set} #[zenoh_macros::unstable] #[inline] pub fn allowed_destination(mut self, destination: Locality) -> Self { @@ -117,12 +122,14 @@ impl PutBuilder<'_, '_> { self } + // tags{session.put.kind.set} pub fn kind(mut self, kind: SampleKind) -> Self { self.kind = kind; self } #[zenoh_macros::unstable] + // tags{session.put.attachment.set} pub fn with_attachment(mut self, attachment: Attachment) -> Self { self.attachment = Some(attachment); self @@ -179,6 +186,7 @@ use zenoh_result::Error; #[zenoh_macros::unstable] #[derive(Clone)] +// ignore_tagging pub enum PublisherRef<'a> { Borrow(&'a Publisher<'a>), Shared(std::sync::Arc>), @@ -235,6 +243,7 @@ impl std::fmt::Debug for PublisherRef<'_> { /// subscriber.stream().map(Ok).forward(publisher).await.unwrap(); /// # }) /// ``` +// tags{publisher} #[derive(Debug, Clone)] pub struct Publisher<'a> { pub(crate) session: SessionRef<'a>, @@ -245,11 +254,13 @@ pub struct Publisher<'a> { } impl<'a> Publisher<'a> { + // tags{publisher.key_expr.get} pub fn key_expr(&self) -> &KeyExpr<'a> { &self.key_expr } /// Change the `congestion_control` to apply when routing the data. + // tags{publisher.congestion_control.set} #[inline] pub fn congestion_control(mut self, congestion_control: CongestionControl) -> Self { self.congestion_control = congestion_control; @@ -257,6 +268,7 @@ impl<'a> Publisher<'a> { } /// Change the priority of the written data. + // tags{publisher.priority.set} #[inline] pub fn priority(mut self, priority: Priority) -> Self { self.priority = priority; @@ -265,6 +277,7 @@ impl<'a> Publisher<'a> { /// Restrict the matching subscribers that will receive the published data /// to the ones that have the given [`Locality`](crate::prelude::Locality). + // tags{publisher.allowed_destination.set} #[zenoh_macros::unstable] #[inline] pub fn allowed_destination(mut self, destination: Locality) -> Self { @@ -302,6 +315,7 @@ impl<'a> Publisher<'a> { /// }).await; /// # }) /// ``` + // ignore_tagging #[zenoh_macros::unstable] pub fn into_arc(self) -> std::sync::Arc { std::sync::Arc::new(self) @@ -329,6 +343,8 @@ impl<'a> Publisher<'a> { /// publisher.write(SampleKind::Put, "value").res().await.unwrap(); /// # }) /// ``` + // tags{publisher.write.kind} + // tags{publisher.write.value} pub fn write(&self, kind: SampleKind, value: IntoValue) -> Publication where IntoValue: Into, @@ -348,6 +364,7 @@ impl<'a> Publisher<'a> { /// publisher.put("value").res().await.unwrap(); /// # }) /// ``` + // tags{publisher.put} #[inline] pub fn put(&self, value: IntoValue) -> Publication where @@ -368,6 +385,7 @@ impl<'a> Publisher<'a> { /// publisher.delete().res().await.unwrap(); /// # }) /// ``` + // tags{publisher.delete} pub fn delete(&self) -> Publication { self._write(SampleKind::Delete, Value::empty()) } @@ -392,6 +410,7 @@ impl<'a> Publisher<'a> { /// .matching_subscribers(); /// # }) /// ``` + // tags{publisher.matching_subscribers.get} #[zenoh_macros::unstable] pub fn matching_status(&self) -> impl Resolve> + '_ { zenoh_core::ResolveFuture::new(async move { @@ -422,6 +441,7 @@ impl<'a> Publisher<'a> { /// } /// # }) /// ``` + // tags{publisher.matching_listener.create} #[zenoh_macros::unstable] pub fn matching_listener(&self) -> MatchingListenerBuilder<'_, DefaultHandler> { MatchingListenerBuilder { @@ -442,6 +462,7 @@ impl<'a> Publisher<'a> { /// publisher.undeclare().res().await.unwrap(); /// # }) /// ``` + // tags{publisher.undeclare} pub fn undeclare(self) -> impl Resolve> + 'a { Undeclarable::undeclare_inner(self, ()) } @@ -475,6 +496,7 @@ impl<'a> Publisher<'a> { /// }).await; /// # }) /// ``` +// ignore_tagging #[zenoh_macros::unstable] pub trait PublisherDeclarations { /// # Examples @@ -550,6 +572,7 @@ impl<'a> Undeclarable<(), PublisherUndeclaration<'a>> for Publisher<'a> { /// publisher.undeclare().res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct PublisherUndeclaration<'a> { publisher: Publisher<'a>, @@ -593,6 +616,7 @@ impl Drop for Publisher<'_> { /// A [`Resolvable`] returned by [`Publisher::put()`](Publisher::put), /// [`Publisher::delete()`](Publisher::delete) and [`Publisher::write()`](Publisher::write). +// tags{} #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct Publication<'a> { publisher: &'a Publisher<'a>, @@ -603,6 +627,7 @@ pub struct Publication<'a> { } impl<'a> Publication<'a> { + // tags{publisher.write.attachment} #[zenoh_macros::unstable] pub fn with_attachment(mut self, attachment: Attachment) -> Self { self.attachment = Some(attachment); @@ -678,6 +703,7 @@ where /// .unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct PublisherBuilder<'a, 'b: 'a> { @@ -705,6 +731,7 @@ impl<'a, 'b> Clone for PublisherBuilder<'a, 'b> { impl<'a, 'b> PublisherBuilder<'a, 'b> { /// Change the `congestion_control` to apply when routing the data. + // tags{publisher.congestion_control.set} #[inline] pub fn congestion_control(mut self, congestion_control: CongestionControl) -> Self { self.congestion_control = congestion_control; @@ -712,6 +739,7 @@ impl<'a, 'b> PublisherBuilder<'a, 'b> { } /// Change the priority of the written data. + // tags{publisher.priority.set} #[inline] pub fn priority(mut self, priority: Priority) -> Self { self.priority = priority; @@ -720,6 +748,7 @@ impl<'a, 'b> PublisherBuilder<'a, 'b> { /// Restrict the matching subscribers that will receive the published data /// to the ones that have the given [`Locality`](crate::prelude::Locality). + // tags{publisher.allowed_destination.set} #[zenoh_macros::unstable] #[inline] pub fn allowed_destination(mut self, destination: Locality) -> Self { @@ -873,6 +902,7 @@ fn resolve_put( } /// The Priority of zenoh messages. +// tags{publisher.priority} #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum Priority { @@ -888,10 +918,13 @@ pub enum Priority { impl Priority { /// The lowest Priority + // tags{publisher.priority.min} pub const MIN: Self = Self::Background; /// The highest Priority + // tags{publisher.priority.max} pub const MAX: Self = Self::RealTime; /// The number of available priorities + // tags{publisher.priority.num} pub const NUM: usize = 1 + Self::MIN as usize - Self::MAX as usize; } @@ -951,6 +984,7 @@ impl From for zenoh_protocol::core::Priority { /// let matching_status = publisher.matching_status().res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[zenoh_macros::unstable] #[derive(Copy, Clone, Debug)] pub struct MatchingStatus { @@ -976,12 +1010,14 @@ impl MatchingStatus { /// .matching_subscribers(); /// # }) /// ``` + // tags{} pub fn matching_subscribers(&self) -> bool { self.matching } } /// A builder for initializing a [`MatchingListener`]. +// ignore_tagging #[zenoh_macros::unstable] #[derive(Debug)] pub struct MatchingListenerBuilder<'a, Handler> { @@ -1014,6 +1050,7 @@ impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // ignore_tagging #[inline] #[zenoh_macros::unstable] pub fn callback(self, callback: Callback) -> MatchingListenerBuilder<'a, Callback> @@ -1048,6 +1085,7 @@ impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // ignore_tagging #[inline] #[zenoh_macros::unstable] pub fn callback_mut( @@ -1084,6 +1122,7 @@ impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { /// } /// # }) /// ``` + // ignore_tagging #[inline] #[zenoh_macros::unstable] pub fn with(self, handler: Handler) -> MatchingListenerBuilder<'a, Handler> @@ -1173,6 +1212,7 @@ pub(crate) struct MatchingListenerInner<'a> { #[zenoh_macros::unstable] impl<'a> MatchingListenerInner<'a> { #[inline] + // ignore_tagging pub fn undeclare(self) -> MatchingListenerUndeclaration<'a> { Undeclarable::undeclare_inner(self, ()) } @@ -1205,6 +1245,7 @@ impl<'a> Undeclarable<(), MatchingListenerUndeclaration<'a>> for MatchingListene /// } /// # }) /// ``` +// tags{publisher.matching_listener} #[zenoh_macros::unstable] pub struct MatchingListener<'a, Receiver> { pub(crate) listener: MatchingListenerInner<'a>, @@ -1229,6 +1270,7 @@ impl<'a, Receiver> MatchingListener<'a, Receiver> { /// matching_listener.undeclare().res().await.unwrap(); /// # }) /// ``` + // tags{publisher.matching_listener.undeclare} #[inline] pub fn undeclare(self) -> MatchingListenerUndeclaration<'a> { self.listener.undeclare() @@ -1257,6 +1299,7 @@ impl std::ops::DerefMut for MatchingListener<'_, Receiver> { } } +// ignore_tagging #[zenoh_macros::unstable] pub struct MatchingListenerUndeclaration<'a> { subscriber: MatchingListenerInner<'a>, diff --git a/zenoh/src/query.rs b/zenoh/src/query.rs index c4f3fb35e9..5d02fc4f57 100644 --- a/zenoh/src/query.rs +++ b/zenoh/src/query.rs @@ -32,6 +32,7 @@ pub use zenoh_protocol::core::QueryTarget; pub use zenoh_protocol::core::ConsolidationMode; /// The operation: either manual or automatic. +// ignore_tagging #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Mode { Auto, @@ -39,6 +40,7 @@ pub enum Mode { } /// The replies consolidation strategy to apply on replies to a [`get`](Session::get). +// tags{session.query_reply.consolidation_mode} #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct QueryConsolidation { pub(crate) mode: Mode, @@ -46,6 +48,7 @@ pub struct QueryConsolidation { impl QueryConsolidation { /// Automatic query consolidation strategy selection. + // tags{session.query_reply.consolidation_mode.auto} pub const AUTO: Self = Self { mode: Mode::Auto }; pub(crate) const fn from_mode(mode: ConsolidationMode) -> Self { @@ -55,6 +58,7 @@ impl QueryConsolidation { } /// Returns the requested [`ConsolidationMode`]. + // ignore_tagging pub fn mode(&self) -> Mode { self.mode } @@ -77,12 +81,15 @@ impl Default for QueryConsolidation { } /// Structs returned by a [`get`](Session::get). +// tags{reply} #[non_exhaustive] #[derive(Clone, Debug)] pub struct Reply { /// The result of this Reply. + // tags{reply.sample} pub sample: Result, /// The id of the zenoh instance that answered this Reply. + // tags{reply.replier_id} pub replier_id: ZenohId, } @@ -116,6 +123,7 @@ pub(crate) struct QueryState { /// } /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct GetBuilder<'a, 'b, Handler> { @@ -149,6 +157,7 @@ impl<'a, 'b> GetBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{session.query_reply.callback} #[inline] pub fn callback(self, callback: Callback) -> GetBuilder<'a, 'b, Callback> where @@ -202,6 +211,7 @@ impl<'a, 'b> GetBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{session.query_reply.callback} #[inline] pub fn callback_mut( self, @@ -232,6 +242,7 @@ impl<'a, 'b> GetBuilder<'a, 'b, DefaultHandler> { /// } /// # }) /// ``` + // tags{session.query_reply.pipe} #[inline] pub fn with(self, handler: Handler) -> GetBuilder<'a, 'b, Handler> where @@ -267,6 +278,7 @@ impl<'a, 'b> GetBuilder<'a, 'b, DefaultHandler> { } impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { /// Change the target of the query. + // tags{session.query_reply.target.set} #[inline] pub fn target(mut self, target: QueryTarget) -> Self { self.target = target; @@ -274,6 +286,7 @@ impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { } /// Change the consolidation mode of the query. + // tags{session.query_reply.consolidation_mode.set} #[inline] pub fn consolidation>(mut self, consolidation: QC) -> Self { self.consolidation = consolidation.into(); @@ -282,6 +295,7 @@ impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { /// Restrict the matching queryables that will receive the query /// to the ones that have the given [`Locality`](crate::prelude::Locality). + // tags{session.query_reply.allowed_destination.set} #[zenoh_macros::unstable] #[inline] pub fn allowed_destination(mut self, destination: Locality) -> Self { @@ -290,6 +304,7 @@ impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { } /// Set query timeout. + // tags{session.query_reply.timeout.set} #[inline] pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = timeout; @@ -297,6 +312,7 @@ impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { } /// Set query value. + // tags{session.query_reply.value.set} #[inline] pub fn with_value(mut self, value: IntoValue) -> Self where @@ -307,6 +323,7 @@ impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { } #[zenoh_macros::unstable] + // tags{session.query_reply.attachment.set} pub fn with_attachment(mut self, attachment: Attachment) -> Self { self.attachment = Some(attachment); self @@ -317,6 +334,7 @@ impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { /// /// If allowed to through `accept_replies(ReplyKeyExpr::Any)`, queryables may also reply on key /// expressions that don't intersect with the query's. + // tags{session.query_reply.accept_replies.set} #[zenoh_macros::unstable] pub fn accept_replies(self, accept: ReplyKeyExpr) -> Self { let Self { @@ -348,10 +366,12 @@ impl<'a, 'b, Handler> GetBuilder<'a, 'b, Handler> { pub(crate) const _REPLY_KEY_EXPR_ANY_SEL_PARAM: &str = "_anyke"; #[zenoh_macros::unstable] +// tags{session.query_reply.reply_key_expr_any_sel_param} pub const REPLY_KEY_EXPR_ANY_SEL_PARAM: &str = _REPLY_KEY_EXPR_ANY_SEL_PARAM; #[zenoh_macros::unstable] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +// tags{session.query_reply.accept_replies} pub enum ReplyKeyExpr { Any, MatchingQuery, diff --git a/zenoh/src/queryable.rs b/zenoh/src/queryable.rs index 9ee73d1641..f830988cd4 100644 --- a/zenoh/src/queryable.rs +++ b/zenoh/src/queryable.rs @@ -63,6 +63,7 @@ impl Drop for QueryInner { } /// Structs received by a [`Queryable`]. +// tags{query} #[derive(Clone)] pub struct Query { pub(crate) inner: Arc, @@ -70,6 +71,7 @@ pub struct Query { impl Query { /// The full [`Selector`] of this Query. + // tags{query.selector.get} #[inline(always)] pub fn selector(&self) -> Selector<'_> { Selector { @@ -79,23 +81,27 @@ impl Query { } /// The key selector part of this Query. + // tags{query.selector.key_expr.get} #[inline(always)] pub fn key_expr(&self) -> &KeyExpr<'static> { &self.inner.key_expr } /// This Query's selector parameters. + // tags{query.selector.parameters.get} #[inline(always)] pub fn parameters(&self) -> &str { &self.inner.parameters } /// This Query's value. + // tags{query.value.get} #[inline(always)] pub fn value(&self) -> Option<&Value> { self.inner.value.as_ref() } + // tags{query.attachment.get} #[zenoh_macros::unstable] pub fn attachment(&self) -> Option<&Attachment> { self.inner.attachment.as_ref() @@ -106,6 +112,7 @@ impl Query { /// By default, queries only accept replies whose key expression intersects with the query's. /// Unless the query has enabled disjoint replies (you can check this through [`Query::accepts_replies`]), /// replying on a disjoint key expression will result in an error when resolving the reply. + // tags{query.reply} #[inline(always)] pub fn reply(&self, result: Result) -> ReplyBuilder<'_> { ReplyBuilder { @@ -116,6 +123,7 @@ impl Query { /// Queries may or may not accept replies on key expressions that do not intersect with their own key expression. /// This getter allows you to check whether or not a specific query does. + // tags{query.accepts_replies.get} #[zenoh_macros::unstable] pub fn accepts_replies(&self) -> ZResult { self._accepts_any_replies().map(|any| { @@ -154,6 +162,7 @@ impl fmt::Display for Query { } /// A builder returned by [`Query::reply()`](Query::reply). +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct ReplyBuilder<'a> { @@ -164,6 +173,7 @@ pub struct ReplyBuilder<'a> { impl<'a> ReplyBuilder<'a> { #[allow(clippy::result_large_err)] #[zenoh_macros::unstable] + // tags{query.reply.attachment.set} pub fn with_attachment(mut self, attachment: Attachment) -> Result { match &mut self.result { Ok(sample) => { @@ -363,6 +373,7 @@ impl<'a> Undeclarable<(), QueryableUndeclaration<'a>> for CallbackQueryable<'a> /// queryable.undeclare().res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct QueryableUndeclaration<'a> { queryable: CallbackQueryable<'a>, @@ -409,6 +420,7 @@ impl Drop for CallbackQueryable<'_> { /// let queryable = session.declare_queryable("key/expression").res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct QueryableBuilder<'a, 'b, Handler> { @@ -436,6 +448,7 @@ impl<'a, 'b> QueryableBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{queryable.callback} #[inline] pub fn callback(self, callback: Callback) -> QueryableBuilder<'a, 'b, Callback> where @@ -477,6 +490,7 @@ impl<'a, 'b> QueryableBuilder<'a, 'b, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{queryable.callback} #[inline] pub fn callback_mut( self, @@ -507,6 +521,7 @@ impl<'a, 'b> QueryableBuilder<'a, 'b, DefaultHandler> { /// } /// # }) /// ``` + // tags{queryable.pipe} #[inline] pub fn with(self, handler: Handler) -> QueryableBuilder<'a, 'b, Handler> where @@ -530,6 +545,7 @@ impl<'a, 'b> QueryableBuilder<'a, 'b, DefaultHandler> { /// Restrict the matching queries that will be receive by this [`Queryable`] /// to the ones that have the given [`Locality`](crate::prelude::Locality). + // tags{queryable.allowed_origin} #[inline] #[zenoh_macros::unstable] pub fn allowed_origin(mut self, origin: Locality) -> Self { @@ -539,6 +555,7 @@ impl<'a, 'b> QueryableBuilder<'a, 'b, DefaultHandler> { } impl<'a, 'b, Handler> QueryableBuilder<'a, 'b, Handler> { /// Change queryable completeness. + // tags{queryable.complete} #[inline] pub fn complete(mut self, complete: bool) -> Self { self.complete = complete; @@ -576,6 +593,7 @@ impl<'a, 'b, Handler> QueryableBuilder<'a, 'b, Handler> { /// } /// # }) /// ``` +// ignore_tagging #[non_exhaustive] #[derive(Debug)] pub struct Queryable<'a, Receiver> { @@ -585,6 +603,7 @@ pub struct Queryable<'a, Receiver> { impl<'a, Receiver> Queryable<'a, Receiver> { #[inline] + // tags{queryable.undeclare} pub fn undeclare(self) -> impl Resolve> + 'a { Undeclarable::undeclare_inner(self, ()) } diff --git a/zenoh/src/sample.rs b/zenoh/src/sample.rs index 5d707e5936..3e9a7fa576 100644 --- a/zenoh/src/sample.rs +++ b/zenoh/src/sample.rs @@ -26,6 +26,7 @@ use zenoh_protocol::core::Encoding; pub type SourceSn = u64; /// The locality of samples to be received by subscribers or targeted by publishers. +// tags{options.locality} #[zenoh_macros::unstable] #[derive(Clone, Copy, Debug, Default, Serialize, PartialEq, Eq)] pub enum Locality { @@ -53,12 +54,15 @@ pub(crate) struct DataInfo { } /// Informations on the source of a zenoh [`Sample`]. +// tags{sample.source_info} #[zenoh_macros::unstable] #[derive(Debug, Clone)] pub struct SourceInfo { /// The [`ZenohId`] of the zenoh instance that published the concerned [`Sample`]. + // tags{sample.source_info.source_id} pub source_id: Option, /// The sequence number of the [`Sample`] from the source. + // tags{sample.source_info.source_sn} pub source_sn: Option, } @@ -111,6 +115,7 @@ mod attachment { use zenoh_protocol::zenoh::ext::AttachmentType; /// A builder for [`Attachment`] + // ignore_tagging #[zenoh_macros::unstable] #[derive(Debug)] pub struct AttachmentBuilder { @@ -124,6 +129,7 @@ mod attachment { } #[zenoh_macros::unstable] impl AttachmentBuilder { + // ignore_tagging pub fn new() -> Self { Self { inner: Vec::new() } } @@ -136,6 +142,7 @@ mod attachment { /// Inserts a key-value pair to the attachment. /// /// Note that [`Attachment`] is a list of non-unique key-value pairs: inserting at the same key multiple times leads to both values being transmitted for that key. + // tags{sample.attachment.insert} pub fn insert + ?Sized, Value: AsRef<[u8]> + ?Sized>( &mut self, key: &Key, @@ -143,6 +150,7 @@ mod attachment { ) { self._insert(key.as_ref(), value.as_ref()) } + // ignore_tagging pub fn build(self) -> Attachment { Attachment { inner: self.inner.into(), @@ -157,6 +165,7 @@ mod attachment { } } } + // tags{sample.attachment} #[zenoh_macros::unstable] #[derive(Clone)] pub struct Attachment { @@ -182,17 +191,21 @@ mod attachment { } #[zenoh_macros::unstable] impl Attachment { + // tags{sample.attachment.create} pub fn new() -> Self { Self { inner: ZBuf::empty(), } } + // tags{sample.attachment.is_empty} pub fn is_empty(&self) -> bool { self.len() == 0 } + // tags{sample.attachment.len} pub fn len(&self) -> usize { self.iter().count() } + // ignore_tagging pub fn iter(&self) -> AttachmentIterator { self.into_iter() } @@ -200,6 +213,7 @@ mod attachment { self.iter() .find_map(|(k, v)| (k.as_slice() == key).then_some(v)) } + // tags{sample.attachment.get} pub fn get>(&self, key: &Key) -> Option { self._get(key.as_ref()) } @@ -214,6 +228,7 @@ mod attachment { /// Note that [`Attachment`] is a list of non-unique key-value pairs: inserting at the same key multiple times leads to both values being transmitted for that key. /// /// [`Attachment`] is not very efficient at inserting, so if you wish to perform multiple inserts, it's generally better to [`Attachment::extend`] after performing the inserts on an [`AttachmentBuilder`] + // tags{sample.attachment.insert} pub fn insert + ?Sized, Value: AsRef<[u8]> + ?Sized>( &mut self, key: &Key, @@ -227,11 +242,13 @@ mod attachment { } self } + // tags{sample.attachment.extend} pub fn extend(&mut self, with: impl Into) -> &mut Self { let with = with.into(); self._extend(with) } } + // ignore_tagging #[zenoh_macros::unstable] pub struct AttachmentIterator<'a> { reader: ZBufReader<'a>, @@ -311,10 +328,12 @@ mod attachment { } } } + #[zenoh_macros::unstable] pub use attachment::{Attachment, AttachmentBuilder, AttachmentIterator}; /// A zenoh sample. +// tags{sample} #[non_exhaustive] #[derive(Clone, Debug)] pub struct Sample { @@ -350,6 +369,7 @@ pub struct Sample { impl Sample { /// Creates a new Sample. + // tags{sample.create} #[inline] pub fn new(key_expr: IntoKeyExpr, value: IntoValue) -> Self where @@ -368,6 +388,7 @@ impl Sample { } } /// Creates a new Sample. + // tags{sample.create.from_value} #[inline] pub fn try_from( key_expr: TryIntoKeyExpr, @@ -391,6 +412,7 @@ impl Sample { } /// Creates a new Sample with optional data info. + // tags{sample.create.with_info} #[inline] pub(crate) fn with_info( key_expr: KeyExpr<'static>, @@ -427,12 +449,14 @@ impl Sample { } /// Gets the timestamp of this Sample. + // tags{sample.timestamp.get} #[inline] pub fn get_timestamp(&self) -> Option<&Timestamp> { self.timestamp.as_ref() } /// Sets the timestamp of this Sample. + // tags{sample.timestamp.set} #[inline] pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self { self.timestamp = Some(timestamp); @@ -440,6 +464,7 @@ impl Sample { } /// Sets the source info of this Sample. + // tags{sample.source_info.set} #[zenoh_macros::unstable] #[inline] pub fn with_source_info(mut self, source_info: SourceInfo) -> Self { @@ -451,6 +476,7 @@ impl Sample { /// Ensure that an associated Timestamp is present in this Sample. /// If not, a new one is created with the current system time and 0x00 as id. /// Get the timestamp of this sample (either existing one or newly created) + // tags{sample.ensure_timestamp} pub fn ensure_timestamp(&mut self) -> &Timestamp { if let Some(ref timestamp) = self.timestamp { timestamp @@ -462,17 +488,21 @@ impl Sample { } #[zenoh_macros::unstable] + // tags{sample.attachment.get} pub fn attachment(&self) -> Option<&Attachment> { self.attachment.as_ref() } #[zenoh_macros::unstable] + // tags{sample.attachment.get} + // tags{sample.attachment.set} pub fn attachment_mut(&mut self) -> &mut Option { &mut self.attachment } #[allow(clippy::result_large_err)] #[zenoh_macros::unstable] + // tags{sample.attachment.set} pub fn with_attachment(mut self, attachment: Attachment) -> Self { self.attachment = Some(attachment); self diff --git a/zenoh/src/scouting.rs b/zenoh/src/scouting.rs index ea09823ea1..1633f02440 100644 --- a/zenoh/src/scouting.rs +++ b/zenoh/src/scouting.rs @@ -44,6 +44,7 @@ pub use zenoh_protocol::scouting::Hello; /// } /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct ScoutBuilder { @@ -68,6 +69,7 @@ impl ScoutBuilder { /// .unwrap(); /// # }) /// ``` + // tags{scout.callback} #[inline] pub fn callback(self, callback: Callback) -> ScoutBuilder where @@ -104,6 +106,7 @@ impl ScoutBuilder { /// .unwrap(); /// # }) /// ``` + /// tags{scout.callback} #[inline] pub fn callback_mut( self, @@ -133,6 +136,7 @@ impl ScoutBuilder { /// } /// # }) /// ``` + // tags{scout.pipe} #[inline] pub fn with(self, handler: Handler) -> ScoutBuilder where @@ -219,7 +223,7 @@ impl ScoutInner { /// scout.stop(); /// # }) /// ``` - pub fn stop(self) { + pub(crate) fn stop(self) { // This drops the inner `stop_sender` and hence stops the scouting receiver std::mem::drop(self); } @@ -249,6 +253,7 @@ impl fmt::Debug for ScoutInner { /// } /// # }) /// ``` +// tags{scout} #[non_exhaustive] #[derive(Debug)] pub struct Scout { @@ -282,6 +287,7 @@ impl Scout { /// scout.stop(); /// # }) /// ``` + // tags{scout.stop} pub fn stop(self) { self.scout.stop() } diff --git a/zenoh/src/selector.rs b/zenoh/src/selector.rs index 2a9a38c02c..dc63b2ac9c 100644 --- a/zenoh/src/selector.rs +++ b/zenoh/src/selector.rs @@ -61,6 +61,7 @@ use std::{ /// 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. +// tags{selector} #[non_exhaustive] #[derive(Clone, PartialEq, Eq)] pub struct Selector<'a> { @@ -70,13 +71,16 @@ pub struct Selector<'a> { pub(crate) parameters: Cow<'a, str>, } +// tags{selector.time_range_key} pub const TIME_RANGE_KEY: &str = "_time"; impl<'a> Selector<'a> { /// Gets the parameters as a raw string. + // tags{selector.parameters.get} pub fn parameters(&self) -> &str { &self.parameters } /// Extracts the selector parameters into a hashmap, returning an error in case of duplicated parameter names. + // tags{selector.parameters.get} pub fn parameters_map(&'a self) -> ZResult> where K: AsRef + std::hash::Hash + std::cmp::Eq, @@ -86,10 +90,12 @@ impl<'a> Selector<'a> { self.decode_into_map() } /// Extracts the selector parameters' name-value pairs into a hashmap, returning an error in case of duplicated parameters. + // tags{selector.parameters.get} pub fn parameters_cowmap(&'a self) -> ZResult, Cow<'a, str>>> { self.decode_into_map() } /// Extracts the selector parameters' name-value pairs into a hashmap, returning an error in case of duplicated parameters. + // tags{selector.parameters.get} pub fn parameters_stringmap(&'a self) -> ZResult> { self.decode_into_map() } @@ -97,6 +103,7 @@ impl<'a> Selector<'a> { /// /// Note that calling this function may cause an allocation and copy if the selector's parameters wasn't /// already owned by `self`. `self` owns its parameters as soon as this function returns. + // tags{selector.parameters.set} pub fn parameters_mut(&mut self) -> &mut String { if let Cow::Borrowed(s) = self.parameters { self.parameters = Cow::Owned(s.to_owned()) @@ -107,15 +114,18 @@ impl<'a> Selector<'a> { unsafe { std::hint::unreachable_unchecked() } // this is safe because we just replaced the borrowed variant } } + // tags{selector.parameters.set} pub fn set_parameters(&mut self, selector: impl Into>) { self.parameters = selector.into(); } + // ignore_tagging pub fn borrowing_clone(&'a self) -> Self { Selector { key_expr: self.key_expr.clone(), parameters: self.parameters.as_ref().into(), } } + // ignore_tagging pub fn into_owned(self) -> Selector<'static> { Selector { key_expr: self.key_expr.into_owned(), @@ -124,22 +134,26 @@ impl<'a> Selector<'a> { } #[deprecated = "If you have ownership of this selector, prefer `Selector::into_owned`"] + // ignore_tagging pub fn to_owned(&self) -> Selector<'static> { self.borrowing_clone().into_owned() } /// Returns this selectors components as a tuple. + // tags{selector.parameters.get} pub fn split(self) -> (KeyExpr<'a>, Cow<'a, str>) { (self.key_expr, self.parameters) } /// Sets the `parameters` part of this `Selector`. #[inline(always)] + // tags{selector.parameters.set} pub fn with_parameters(mut self, parameters: &'a str) -> Self { self.parameters = parameters.into(); self } + // tags{selector.parameters.set} pub fn extend<'b, I, K, V>(&'b mut self, parameters: I) where I: IntoIterator, @@ -154,6 +168,7 @@ impl<'a> Selector<'a> { } /// Sets the time range targeted by the selector. + // tags{selector.parameters.timerange.set} pub fn with_time_range(&mut self, time_range: TimeRange) { self.remove_time_range(); let selector = self.parameters_mut(); @@ -164,6 +179,7 @@ impl<'a> Selector<'a> { write!(selector, "{TIME_RANGE_KEY}={time_range}").unwrap(); // This unwrap is safe because `String: Write` should be infallibe. } + // tags{selector.parameters.timerange.remove} pub fn remove_time_range(&mut self) { let selector = self.parameters_mut(); @@ -281,10 +297,13 @@ fn selector_accessors() { assert_eq!(selector.to_string(), without_any + "&other"); } } +// tags{selector.parameter} pub trait Parameter: Sized { type Name: AsRef + Sized; type Value: AsRef + Sized; + // tags{selector.parameter.name.get} fn name(&self) -> &Self::Name; + // tags{selector.parameter.value.get} fn value(&self) -> &Self::Value; fn split(self) -> (Self::Name, Self::Value); fn extract_name(self) -> Self::Name { @@ -321,6 +340,7 @@ type ExtractedValue<'a, VS: Parameters<'a>> = <::Item a /// A trait 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. +// tags{selector.parameters} pub trait Parameters<'a> { type Decoder: Iterator + 'a; /// Returns this selector's parameters as an iterator. @@ -329,6 +349,7 @@ pub trait Parameters<'a> { ::Item: Parameter; /// Extracts all parameters into a HashMap, returning an error if duplicate parameters arrise. + // tags{selector.parameters.get} fn decode_into_map(&'a self) -> ZResult> where ::Item: Parameter, @@ -353,6 +374,7 @@ pub trait Parameters<'a> { /// Extracts the requested parameters from the selector parameters. /// /// The default implementation is done in a single pass through the selector parameters, returning an error if any of the requested parameters are present more than once. + // tags{selector.parameters.get} fn get_parameters( &'a self, names: [&str; N], @@ -383,6 +405,7 @@ pub trait Parameters<'a> { /// Extracts the requested arguments from the selector parameters as booleans, following the Zenoh convention that if a parameter name is present and has a value different from "false", its value is truthy. /// /// The default implementation is done in a single pass through the selector parameters, returning an error if some of the requested parameters are present more than once. + // tags{selector.parameters.get.bools} fn get_bools(&'a self, names: [&str; N]) -> ZResult<[bool; N]> where ::Item: Parameter, @@ -396,6 +419,7 @@ pub trait Parameters<'a> { /// 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. + // tags{selector.parameters.timerange.get} fn time_range(&'a self) -> ZResult> where ::Item: Parameter, @@ -406,12 +430,14 @@ pub trait Parameters<'a> { }) } } +// tags{selector.parameters.get} impl<'a> Parameters<'a> for Selector<'a> { type Decoder = >::Decoder; fn decode(&'a self) -> Self::Decoder { self.parameters().decode() } } +// tags{selector.parameters.create.from_str} impl<'a> Parameters<'a> for str { type Decoder = form_urlencoded::Parse<'a>; fn decode(&'a self) -> Self::Decoder { @@ -419,6 +445,7 @@ impl<'a> Parameters<'a> for str { } } +// tags{selector.parameters.create.from_hashmap} impl<'a, K: Borrow + Hash + Eq + 'a, V: Borrow + 'a> Parameters<'a> for HashMap { type Decoder = std::collections::hash_map::Iter<'a, K, V>; fn decode(&'a self) -> Self::Decoder { @@ -458,6 +485,7 @@ impl<'a> From<&Selector<'a>> for Selector<'a> { } } +// tags{selector.create.from_str} impl TryFrom for Selector<'_> { type Error = zenoh_result::Error; fn try_from(mut s: String) -> Result { @@ -472,6 +500,7 @@ impl TryFrom for Selector<'_> { } } +// tags{selector.create.from_str} impl<'a> TryFrom<&'a str> for Selector<'a> { type Error = zenoh_result::Error; fn try_from(s: &'a str) -> Result { @@ -484,6 +513,7 @@ impl<'a> TryFrom<&'a str> for Selector<'a> { } } } +// tags{selector.create.from_str} impl FromStr for Selector<'static> { type Err = zenoh_result::Error; fn from_str(s: &str) -> Result { @@ -491,6 +521,7 @@ impl FromStr for Selector<'static> { } } +// tags{selector.create.from_str} impl<'a> TryFrom<&'a String> for Selector<'a> { type Error = zenoh_result::Error; fn try_from(s: &'a String) -> Result { @@ -498,6 +529,7 @@ impl<'a> TryFrom<&'a String> for Selector<'a> { } } +// tags{selector.create.from_query} impl<'a> From<&'a Query> for Selector<'a> { fn from(q: &'a Query) -> Self { Selector { @@ -507,6 +539,7 @@ impl<'a> From<&'a Query> for Selector<'a> { } } +// tags{selector.create.from_keyexpr} impl<'a> From<&KeyExpr<'a>> for Selector<'a> { fn from(key_selector: &KeyExpr<'a>) -> Self { Self { @@ -516,6 +549,7 @@ impl<'a> From<&KeyExpr<'a>> for Selector<'a> { } } +// tags{selector.create.from_keyexpr} impl<'a> From<&'a keyexpr> for Selector<'a> { fn from(key_selector: &'a keyexpr) -> Self { Self { @@ -525,6 +559,7 @@ impl<'a> From<&'a keyexpr> for Selector<'a> { } } +// tags{selector.create.from_keyexpr} impl<'a> From<&'a OwnedKeyExpr> for Selector<'a> { fn from(key_selector: &'a OwnedKeyExpr) -> Self { Self { @@ -534,6 +569,7 @@ impl<'a> From<&'a OwnedKeyExpr> for Selector<'a> { } } +// tags{selector.create.from_keyexpr} impl From for Selector<'static> { fn from(key_selector: OwnedKeyExpr) -> Self { Self { @@ -543,6 +579,7 @@ impl From for Selector<'static> { } } +// tags{selector.create.from_keyexpr} impl<'a> From> for Selector<'a> { fn from(key_selector: KeyExpr<'a>) -> Self { Self { diff --git a/zenoh/src/session.rs b/zenoh/src/session.rs index d52c446d3d..d220fcb9c6 100644 --- a/zenoh/src/session.rs +++ b/zenoh/src/session.rs @@ -278,6 +278,7 @@ impl Resource { } } +// ignore_tagging #[derive(Clone)] pub enum SessionRef<'a> { Borrow(&'a Session), @@ -368,6 +369,7 @@ impl fmt::Debug for SessionRef<'_> { } /// A trait implemented by types that can be undeclared. +// ignore_tagging pub trait Undeclarable> where O: Resolve + Send, @@ -387,6 +389,7 @@ where /// A zenoh session. /// +// tags{session} pub struct Session { pub(crate) runtime: Runtime, pub(crate) state: Arc>, @@ -453,6 +456,7 @@ impl Session { /// }).await; /// # }) /// ``` + // tags{} pub fn into_arc(self) -> Arc { Arc::new(self) } @@ -484,16 +488,19 @@ impl Session { /// }).await; /// # }) /// ``` + // tags{} pub fn leak(s: Self) -> &'static mut Self { Box::leak(Box::new(s)) } /// Returns the identifier of the current session. `zid()` is a convenient shortcut. /// See [`Session::info()`](`Session::info()`) and [`SessionInfo::zid()`](`SessionInfo::zid()`) for more details. + // tags{session.zid.get} pub fn zid(&self) -> ZenohId { self.info().zid().res_sync() } + // tags{session.clock.get} pub fn hlc(&self) -> Option<&HLC> { self.runtime.hlc() } @@ -512,6 +519,7 @@ impl Session { /// session.close().res().await.unwrap(); /// # }) /// ``` + // tags{session.close} pub fn close(self) -> impl Resolve> { ResolveFuture::new(async move { trace!("close()"); @@ -524,6 +532,7 @@ impl Session { }) } + // tags{} pub fn undeclare<'a, T, O>(&'a self, decl: T) -> O where O: Resolve>, @@ -559,6 +568,8 @@ impl Session { /// let _ = session.config().insert_json5("connect/endpoints", r#"["tcp/127.0.0.1/7447"]"#); /// # }) /// ``` + // tags{session.config.get} + // tags{session.config.subscribe} pub fn config(&self) -> &Notifier { self.runtime.config() } @@ -619,6 +630,7 @@ impl Session { /// let key_expr = session.declare_keyexpr("key/expression").res().await.unwrap(); /// # }) /// ``` + // tags{session.declare_keyexpr} pub fn declare_keyexpr<'a, 'b: 'a, TryIntoKeyExpr>( &'a self, key_expr: TryIntoKeyExpr, @@ -685,6 +697,7 @@ impl Session { /// .unwrap(); /// # }) /// ``` + // tags{session.put} #[inline] pub fn put<'a, 'b: 'a, TryIntoKeyExpr, IntoValue>( &'a self, @@ -720,6 +733,7 @@ impl Session { /// session.delete("key/expression").res().await.unwrap(); /// # }) /// ``` + // tags{session.delete} #[inline] pub fn delete<'a, 'b: 'a, TryIntoKeyExpr>( &'a self, @@ -758,6 +772,7 @@ impl Session { /// } /// # }) /// ``` + // tags{session.query_reply} pub fn get<'a, 'b: 'a, IntoSelector>( &'a self, selector: IntoSelector, @@ -2629,6 +2644,7 @@ pub trait SessionDeclarations<'s, 'a> { /// .unwrap(); /// # }) /// ``` + // tags{liveliness.create} #[zenoh_macros::unstable] fn liveliness(&'s self) -> Liveliness<'a>; /// Get informations about the zenoh [`Session`](Session). diff --git a/zenoh/src/subscriber.rs b/zenoh/src/subscriber.rs index 7258833d28..68981a3a62 100644 --- a/zenoh/src/subscriber.rs +++ b/zenoh/src/subscriber.rs @@ -130,6 +130,7 @@ impl<'a> PullSubscriberInner<'a> { /// subscriber.pull(); /// # }) /// ``` + // tags{subscriber.pull} #[inline] pub fn pull(&self) -> impl Resolve> + '_ { self.inner.session.pull(&self.inner.state.key_expr) @@ -157,6 +158,7 @@ impl<'a> PullSubscriberInner<'a> { /// subscriber.undeclare().res().await.unwrap(); /// # }) /// ``` + // tags{subscriber.undeclare} #[inline] pub fn undeclare(self) -> impl Resolve> + 'a { Undeclarable::undeclare_inner(self.inner, ()) @@ -213,6 +215,7 @@ impl<'a> Undeclarable<(), SubscriberUndeclaration<'a>> for SubscriberInner<'a> { /// subscriber.undeclare().res().await.unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct SubscriberUndeclaration<'a> { subscriber: SubscriberInner<'a>, @@ -248,6 +251,7 @@ impl Drop for SubscriberInner<'_> { } /// The mode for pull subscribers. +// tags{subscriber.options.pull_mode} #[non_exhaustive] #[derive(Debug, Clone, Copy)] pub struct PullMode; @@ -265,6 +269,7 @@ impl From for Mode { } /// The mode for push subscribers. +// tags{subscriber.options.push_mode} #[non_exhaustive] #[derive(Debug, Clone, Copy)] pub struct PushMode; @@ -298,6 +303,7 @@ impl From for Mode { /// .unwrap(); /// # }) /// ``` +// ignore_tagging #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct SubscriberBuilder<'a, 'b, Mode, Handler> { @@ -349,6 +355,7 @@ impl<'a, 'b, Mode> SubscriberBuilder<'a, 'b, Mode, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{subscriber.callback} #[inline] pub fn callback(self, callback: Callback) -> SubscriberBuilder<'a, 'b, Mode, Callback> where @@ -392,6 +399,7 @@ impl<'a, 'b, Mode> SubscriberBuilder<'a, 'b, Mode, DefaultHandler> { /// .unwrap(); /// # }) /// ``` + // tags{subscriber.callback} #[inline] pub fn callback_mut( self, @@ -422,6 +430,7 @@ impl<'a, 'b, Mode> SubscriberBuilder<'a, 'b, Mode, DefaultHandler> { /// } /// # }) /// ``` + // tags{subscriber.pipe} #[inline] pub fn with(self, handler: Handler) -> SubscriberBuilder<'a, 'b, Mode, Handler> where @@ -447,6 +456,7 @@ impl<'a, 'b, Mode> SubscriberBuilder<'a, 'b, Mode, DefaultHandler> { } impl<'a, 'b, Mode, Handler> SubscriberBuilder<'a, 'b, Mode, Handler> { /// Change the subscription reliability. + // tags{subscriber.options.reliability} #[inline] pub fn reliability(mut self, reliability: Reliability) -> Self { self.reliability = reliability; @@ -454,6 +464,7 @@ impl<'a, 'b, Mode, Handler> SubscriberBuilder<'a, 'b, Mode, Handler> { } /// Change the subscription reliability to `Reliable`. + // tags{subscriber.options.reliability} #[inline] pub fn reliable(mut self) -> Self { self.reliability = Reliability::Reliable; @@ -461,6 +472,7 @@ impl<'a, 'b, Mode, Handler> SubscriberBuilder<'a, 'b, Mode, Handler> { } /// Change the subscription reliability to `BestEffort`. + // tags{subscriber.options.reliability} #[inline] pub fn best_effort(mut self) -> Self { self.reliability = Reliability::BestEffort; @@ -469,6 +481,7 @@ impl<'a, 'b, Mode, Handler> SubscriberBuilder<'a, 'b, Mode, Handler> { /// Restrict the matching publications that will be receive by this [`Subscriber`] /// to the ones that have the given [`Locality`](crate::prelude::Locality). + // tags{subscriber.options.locality} #[zenoh_macros::unstable] #[inline] pub fn allowed_origin(mut self, origin: Locality) -> Self { @@ -477,6 +490,7 @@ impl<'a, 'b, Mode, Handler> SubscriberBuilder<'a, 'b, Mode, Handler> { } /// Change the subscription mode to Pull. + // tags{subscriber.options.pull_mode} #[inline] pub fn pull_mode(self) -> SubscriberBuilder<'a, 'b, PullMode, Handler> { let SubscriberBuilder { @@ -498,6 +512,7 @@ impl<'a, 'b, Mode, Handler> SubscriberBuilder<'a, 'b, Mode, Handler> { } /// Change the subscription mode to Push. + // tags{subscriber.options.push_mode} #[inline] pub fn push_mode(self) -> SubscriberBuilder<'a, 'b, PushMode, Handler> { let SubscriberBuilder { @@ -651,6 +666,7 @@ where /// } /// # }) /// ``` +// tags{subscriber} #[non_exhaustive] #[derive(Debug)] pub struct Subscriber<'a, Receiver> { @@ -686,6 +702,7 @@ pub struct Subscriber<'a, Receiver> { /// subscriber.pull(); /// # }) /// ``` +// tags{pull_subscriber} #[non_exhaustive] pub struct PullSubscriber<'a, Receiver> { pub(crate) subscriber: PullSubscriberInner<'a>, @@ -725,6 +742,7 @@ impl<'a, Receiver> PullSubscriber<'a, Receiver> { /// subscriber.pull(); /// # }) /// ``` + // tags{pull_subscriber.pull} #[inline] pub fn pull(&self) -> impl Resolve> + '_ { self.subscriber.pull() @@ -749,6 +767,7 @@ impl<'a, Receiver> PullSubscriber<'a, Receiver> { /// subscriber.undeclare().res().await.unwrap(); /// # }) /// ``` + // tags{pull_subscriber.undeclare} #[inline] pub fn undeclare(self) -> impl Resolve> + 'a { self.subscriber.undeclare() @@ -757,6 +776,7 @@ impl<'a, Receiver> PullSubscriber<'a, Receiver> { impl<'a, Receiver> Subscriber<'a, Receiver> { /// Returns the [`KeyExpr`] this Subscriber subscribes to. + // tags{subscriber.key_expr.get} pub fn key_expr(&self) -> &KeyExpr<'static> { &self.subscriber.state.key_expr } @@ -779,6 +799,7 @@ impl<'a, Receiver> Subscriber<'a, Receiver> { /// subscriber.undeclare().res().await.unwrap(); /// # }) /// ``` + // tags{subscriber.undeclare} #[inline] pub fn undeclare(self) -> SubscriberUndeclaration<'a> { self.subscriber.undeclare() diff --git a/zenoh/src/value.rs b/zenoh/src/value.rs index 849cfd57d5..865e66cf87 100644 --- a/zenoh/src/value.rs +++ b/zenoh/src/value.rs @@ -29,6 +29,7 @@ use crate::prelude::{Encoding, KnownEncoding, Sample, SplitBuffer}; use zenoh_shm::SharedMemoryBuf; /// A zenoh Value. +// tags{value} #[non_exhaustive] #[derive(Clone)] pub struct Value { @@ -40,6 +41,7 @@ pub struct Value { impl Value { /// Creates a new zenoh Value. + // tags{value.create.from_buffer} pub fn new(payload: ZBuf) -> Self { Value { payload, @@ -48,6 +50,7 @@ impl Value { } /// Creates an empty Value. + // tags{value.create.empty} pub fn empty() -> Self { Value { payload: ZBuf::empty(), @@ -56,6 +59,7 @@ impl Value { } /// Sets the encoding of this zenoh Value. + // tags{value.encoding.set} #[inline(always)] pub fn encoding(mut self, encoding: Encoding) -> Self { self.encoding = encoding;