use std::{
fmt,
future::{IntoFuture, Ready},
ops::{Deref, DerefMut},
sync::Arc,
};
use tracing::error;
use zenoh_core::{Resolvable, Resolve, Wait};
use zenoh_protocol::{
core::{EntityId, Parameters, WireExpr, ZenohIdProto},
network::{response, Mapping, RequestId, Response, ResponseFinal},
zenoh::{self, reply::ReplyBody, Del, Put, ResponseBody},
};
use zenoh_result::ZResult;
#[zenoh_macros::unstable]
use {
crate::api::query::ReplyKeyExpr, zenoh_config::wrappers::EntityGlobalId,
zenoh_protocol::core::EntityGlobalIdProto,
};
#[zenoh_macros::unstable]
use crate::api::selector::ZenohParameters;
#[zenoh_macros::internal]
use crate::net::primitives::DummyPrimitives;
use crate::{
api::{
builders::reply::{ReplyBuilder, ReplyBuilderDelete, ReplyBuilderPut, ReplyErrBuilder},
bytes::ZBytes,
encoding::Encoding,
handlers::CallbackParameter,
key_expr::KeyExpr,
sample::{Locality, Sample, SampleKind},
selector::Selector,
session::{UndeclarableSealed, WeakSession},
Id,
},
handlers::Callback,
net::primitives::Primitives,
};
pub(crate) struct QueryInner {
pub(crate) key_expr: KeyExpr<'static>,
pub(crate) parameters: Parameters<'static>,
pub(crate) qid: RequestId,
pub(crate) zid: ZenohIdProto,
pub(crate) primitives: Arc<dyn Primitives>,
}
impl QueryInner {
#[zenoh_macros::internal]
fn empty() -> Self {
QueryInner {
key_expr: KeyExpr::dummy(),
parameters: Parameters::empty(),
qid: 0,
zid: ZenohIdProto::default(),
primitives: Arc::new(DummyPrimitives),
}
}
}
impl Drop for QueryInner {
fn drop(&mut self) {
self.primitives.send_response_final(&mut ResponseFinal {
rid: self.qid,
ext_qos: response::ext::QoSType::RESPONSE_FINAL,
ext_tstamp: None,
});
}
}
#[derive(Clone)]
pub struct Query {
pub(crate) inner: Arc<QueryInner>,
pub(crate) eid: EntityId,
pub(crate) value: Option<(ZBytes, Encoding)>,
pub(crate) attachment: Option<ZBytes>,
}
impl Query {
#[inline(always)]
pub fn selector(&self) -> Selector<'_> {
Selector::borrowed(&self.inner.key_expr, &self.inner.parameters)
}
#[inline(always)]
pub fn key_expr(&self) -> &KeyExpr<'static> {
&self.inner.key_expr
}
#[inline(always)]
pub fn parameters(&self) -> &Parameters<'static> {
&self.inner.parameters
}
#[inline(always)]
pub fn payload(&self) -> Option<&ZBytes> {
self.value.as_ref().map(|v| &v.0)
}
#[inline(always)]
pub fn payload_mut(&mut self) -> Option<&mut ZBytes> {
self.value.as_mut().map(|v| &mut v.0)
}
#[inline(always)]
pub fn encoding(&self) -> Option<&Encoding> {
self.value.as_ref().map(|v| &v.1)
}
pub fn attachment(&self) -> Option<&ZBytes> {
self.attachment.as_ref()
}
pub fn attachment_mut(&mut self) -> Option<&mut ZBytes> {
self.attachment.as_mut()
}
#[inline(always)]
#[zenoh_macros::internal]
pub fn reply_sample(&self, sample: Sample) -> ReplySample<'_> {
ReplySample {
query: self,
sample,
}
}
#[inline(always)]
pub fn reply<'b, TryIntoKeyExpr, IntoZBytes>(
&self,
key_expr: TryIntoKeyExpr,
payload: IntoZBytes,
) -> ReplyBuilder<'_, 'b, ReplyBuilderPut>
where
TryIntoKeyExpr: TryInto<KeyExpr<'b>>,
<TryIntoKeyExpr as TryInto<KeyExpr<'b>>>::Error: Into<zenoh_result::Error>,
IntoZBytes: Into<ZBytes>,
{
ReplyBuilder::<'_, 'b, ReplyBuilderPut>::new(self, key_expr, payload)
}
#[inline(always)]
pub fn reply_err<IntoZBytes>(&self, payload: IntoZBytes) -> ReplyErrBuilder<'_>
where
IntoZBytes: Into<ZBytes>,
{
ReplyErrBuilder::new(self, payload)
}
#[inline(always)]
pub fn reply_del<'b, TryIntoKeyExpr>(
&self,
key_expr: TryIntoKeyExpr,
) -> ReplyBuilder<'_, 'b, ReplyBuilderDelete>
where
TryIntoKeyExpr: TryInto<KeyExpr<'b>>,
<TryIntoKeyExpr as TryInto<KeyExpr<'b>>>::Error: Into<zenoh_result::Error>,
{
ReplyBuilder::<'_, 'b, ReplyBuilderDelete>::new(self, key_expr)
}
#[zenoh_macros::unstable]
pub fn accepts_replies(&self) -> ZResult<ReplyKeyExpr> {
self._accepts_any_replies().map(|any| {
if any {
ReplyKeyExpr::Any
} else {
ReplyKeyExpr::MatchingQuery
}
})
}
#[cfg(feature = "unstable")]
fn _accepts_any_replies(&self) -> ZResult<bool> {
Ok(self.parameters().reply_key_expr_any())
}
#[zenoh_macros::internal]
pub unsafe fn empty() -> Self {
Query {
inner: Arc::new(QueryInner::empty()),
eid: 0,
value: None,
attachment: None,
}
}
}
impl fmt::Debug for Query {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Query")
.field("key_selector", &self.inner.key_expr)
.field("parameters", &self.inner.parameters)
.finish()
}
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Query")
.field(
"selector",
&format!("{}{}", &self.inner.key_expr, &self.inner.parameters),
)
.finish()
}
}
impl CallbackParameter for Query {
type Message<'a> = Self;
fn from_message(msg: Self::Message<'_>) -> Self {
msg
}
}
#[zenoh_macros::internal]
pub struct ReplySample<'a> {
query: &'a Query,
sample: Sample,
}
#[zenoh_macros::internal]
impl Resolvable for ReplySample<'_> {
type To = ZResult<()>;
}
#[zenoh_macros::internal]
impl Wait for ReplySample<'_> {
fn wait(self) -> <Self as Resolvable>::To {
self.query._reply_sample(self.sample)
}
}
#[zenoh_macros::internal]
impl IntoFuture for ReplySample<'_> {
type Output = <Self as Resolvable>::To;
type IntoFuture = Ready<<Self as Resolvable>::To>;
fn into_future(self) -> Self::IntoFuture {
std::future::ready(self.wait())
}
}
impl Query {
pub(crate) fn _reply_sample(&self, sample: Sample) -> ZResult<()> {
let c = zcondfeat!(
"unstable",
!self._accepts_any_replies().unwrap_or(false),
true
);
if c && !self.key_expr().intersects(&sample.key_expr) {
bail!("Attempted to reply on `{}`, which does not intersect with query `{}`, despite query only allowing replies on matching key expressions", sample.key_expr, self.key_expr())
}
#[cfg(not(feature = "unstable"))]
let ext_sinfo = None;
#[cfg(feature = "unstable")]
let ext_sinfo = sample.source_info.into();
self.inner.primitives.send_response(&mut Response {
rid: self.inner.qid,
wire_expr: WireExpr {
scope: 0,
suffix: std::borrow::Cow::Owned(sample.key_expr.into()),
mapping: Mapping::Sender,
},
payload: ResponseBody::Reply(zenoh::Reply {
consolidation: zenoh::ConsolidationMode::DEFAULT,
ext_unknown: vec![],
payload: match sample.kind {
SampleKind::Put => ReplyBody::Put(Put {
timestamp: sample.timestamp,
encoding: sample.encoding.into(),
ext_sinfo,
#[cfg(feature = "shared-memory")]
ext_shm: None,
ext_attachment: sample.attachment.map(|a| a.into()),
ext_unknown: vec![],
payload: sample.payload.into(),
}),
SampleKind::Delete => ReplyBody::Del(Del {
timestamp: sample.timestamp,
ext_sinfo,
ext_attachment: sample.attachment.map(|a| a.into()),
ext_unknown: vec![],
}),
},
}),
ext_qos: sample.qos.into(),
ext_tstamp: None,
ext_respid: Some(response::ext::ResponderIdType {
zid: self.inner.zid,
eid: self.eid,
}),
});
Ok(())
}
}
pub(crate) struct QueryableState {
pub(crate) id: Id,
pub(crate) key_expr: KeyExpr<'static>,
pub(crate) complete: bool,
pub(crate) origin: Locality,
pub(crate) callback: Callback<Query>,
}
impl fmt::Debug for QueryableState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Queryable")
.field("id", &self.id)
.field("key_expr", &self.key_expr)
.field("complete", &self.complete)
.finish()
}
}
#[derive(Debug)]
pub(crate) struct QueryableInner {
pub(crate) session: WeakSession,
pub(crate) id: Id,
pub(crate) undeclare_on_drop: bool,
pub(crate) key_expr: KeyExpr<'static>,
}
#[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
pub struct QueryableUndeclaration<Handler>(Queryable<Handler>);
impl<Handler> Resolvable for QueryableUndeclaration<Handler> {
type To = ZResult<()>;
}
impl<Handler> Wait for QueryableUndeclaration<Handler> {
fn wait(mut self) -> <Self as Resolvable>::To {
self.0.undeclare_impl()
}
}
impl<Handler> IntoFuture for QueryableUndeclaration<Handler> {
type Output = <Self as Resolvable>::To;
type IntoFuture = Ready<<Self as Resolvable>::To>;
fn into_future(self) -> Self::IntoFuture {
std::future::ready(self.wait())
}
}
#[non_exhaustive]
#[derive(Debug)]
pub struct Queryable<Handler> {
pub(crate) inner: QueryableInner,
pub(crate) handler: Handler,
}
impl<Handler> Queryable<Handler> {
#[zenoh_macros::unstable]
pub fn id(&self) -> EntityGlobalId {
EntityGlobalIdProto {
zid: self.inner.session.zid().into(),
eid: self.inner.id,
}
.into()
}
pub fn handler(&self) -> &Handler {
&self.handler
}
pub fn handler_mut(&mut self) -> &mut Handler {
&mut self.handler
}
#[inline]
pub fn undeclare(self) -> impl Resolve<ZResult<()>>
where
Handler: Send,
{
UndeclarableSealed::undeclare_inner(self, ())
}
fn undeclare_impl(&mut self) -> ZResult<()> {
self.inner.undeclare_on_drop = false;
self.inner.session.close_queryable(self.inner.id)
}
#[zenoh_macros::internal]
pub fn set_background(&mut self, background: bool) {
self.inner.undeclare_on_drop = !background;
}
#[inline]
pub fn key_expr(&self) -> &KeyExpr<'static> {
&self.inner.key_expr
}
}
impl<Handler> Drop for Queryable<Handler> {
fn drop(&mut self) {
if self.inner.undeclare_on_drop {
if let Err(error) = self.undeclare_impl() {
error!(error);
}
}
}
}
impl<Handler: Send> UndeclarableSealed<()> for Queryable<Handler> {
type Undeclaration = QueryableUndeclaration<Handler>;
fn undeclare_inner(self, _: ()) -> Self::Undeclaration {
QueryableUndeclaration(self)
}
}
impl<Handler> Deref for Queryable<Handler> {
type Target = Handler;
fn deref(&self) -> &Self::Target {
self.handler()
}
}
impl<Handler> DerefMut for Queryable<Handler> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.handler_mut()
}
}