use crate::{Error, Result};
use zenoh::Session;
use zenoh::handlers::FifoChannelHandler;
use zenoh::liveliness::LivelinessToken;
use zenoh::query::{Query, Queryable};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ReservedError {
InvalidArgs,
Unauthorized,
NotFound,
Unsupported,
Busy,
Gated,
}
impl ReservedError {
pub const ALL: [ReservedError; 6] = [
ReservedError::InvalidArgs,
ReservedError::Unauthorized,
ReservedError::NotFound,
ReservedError::Unsupported,
ReservedError::Busy,
ReservedError::Gated,
];
pub fn name(self) -> &'static str {
match self {
ReservedError::InvalidArgs => "error/invalid-args",
ReservedError::Unauthorized => "error/unauthorized",
ReservedError::NotFound => "error/not-found",
ReservedError::Unsupported => "error/unsupported",
ReservedError::Busy => "error/busy",
ReservedError::Gated => "error/gated",
}
}
pub fn envelope(self, message: &str) -> Vec<u8> {
serde_json::to_vec(&serde_json::json!({
"error": self.name(),
"message": message,
}))
.expect("two string fields serialize")
}
}
pub struct Responder {
key: String,
queryable: Queryable<FifoChannelHandler<Query>>,
}
impl std::fmt::Debug for Responder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Responder")
.field("key", &self.key)
.finish_non_exhaustive()
}
}
impl Responder {
pub fn key(&self) -> &str {
&self.key
}
pub async fn next(&self) -> Option<Query> {
self.queryable.recv_async().await.ok()
}
pub fn stream(&self) -> impl futures_core::Stream<Item = Query> + '_ {
self.queryable.stream()
}
pub async fn reply(
&self,
query: &Query,
payload: Vec<u8>,
encoding: Option<&str>,
) -> Result<()> {
let reply = query.reply(self.key.clone(), payload);
let reply = match encoding {
Some(e) => reply.encoding(e),
None => reply,
};
reply.await.map_err(|e| Error::bus("reply", &self.key, e))
}
pub async fn reply_err(
&self,
query: &Query,
error: ReservedError,
message: &str,
) -> Result<()> {
query
.reply_err(error.envelope(message))
.encoding("application/json")
.await
.map_err(|e| Error::bus("reply_err", &self.key, e))
}
pub async fn undeclare(self) -> Result<()> {
self.queryable
.undeclare()
.await
.map_err(|e| Error::bus("undeclare", &self.key, e))
}
}
#[derive(Debug)]
pub struct BringUp<'a> {
session: &'a Session,
responders: Vec<Responder>,
}
impl<'a> BringUp<'a> {
pub fn new(session: &'a Session) -> Self {
BringUp {
session,
responders: Vec::new(),
}
}
pub async fn serve(&mut self, key: &str) -> Result<&Responder> {
let parsed = zenoh::key_expr::KeyExpr::try_from(key.to_string())
.map_err(|e| Error::bus("declare queryable", key, e))?;
if parsed.is_wild() {
return Err(Error::unaskable(
key.to_string(),
"a producer serves its own concrete key, never a wildcard \
(RFC 05 §2.1 — replies are attributed by their concrete reply \
key)",
));
}
let queryable = crate::bus::teardown::declared(
"declare queryable",
key,
self.session.declare_queryable(parsed).complete(false),
)
.await?;
self.responders.push(Responder {
key: key.to_string(),
queryable,
});
Ok(self.responders.last().expect("just pushed"))
}
pub async fn alive(self, alive_key: &str) -> Result<LiveProducer> {
let token = crate::bus::teardown::declared(
"declare alive token",
alive_key,
self.session
.liveliness()
.declare_token(alive_key.to_string()),
)
.await?;
Ok(LiveProducer {
token: Some(token),
responders: self.responders,
})
}
pub fn without_alive(self) -> Vec<Responder> {
self.responders
}
}
#[derive(Debug)]
pub struct LiveProducer {
token: Option<LivelinessToken>,
pub responders: Vec<Responder>,
}
impl LiveProducer {
pub async fn retire(mut self) -> Result<()> {
if let Some(token) = self.token.take() {
token
.undeclare()
.await
.map_err(|e| Error::bus("retract alive token", "", e))?;
}
let declared: Vec<(String, Responder)> = self
.responders
.drain(..)
.map(|r| (r.key.clone(), r))
.collect();
crate::bus::teardown::drain_undeclare(declared, Responder::undeclare).await
}
}