use bytes::Bytes;
use protobuf::{well_known_types::any::Any, Message, MessageFull};
use std::{error::Error, fmt::Display};
pub use default_notifier::SimpleNotifier;
#[cfg(feature = "usubscription")]
pub use default_pubsub::{InMemorySubscriber, SimplePublisher};
pub use in_memory_rpc_client::InMemoryRpcClient;
pub use in_memory_rpc_server::InMemoryRpcServer;
#[cfg(any(test, feature = "test-util"))]
pub use notification::MockNotifier;
pub use notification::{NotificationError, Notifier};
#[cfg(any(test, feature = "test-util"))]
pub use pubsub::MockSubscriptionChangeHandler;
#[cfg(feature = "usubscription")]
pub use pubsub::{PubSubError, Publisher, Subscriber};
#[cfg(any(test, feature = "test-util"))]
pub use rpc::{MockRequestHandler, MockRpcClient, MockRpcServerImpl};
pub use rpc::{RequestHandler, RpcClient, RpcServer, ServiceInvocationError};
#[cfg(feature = "udiscovery")]
pub use udiscovery_client::RpcClientUDiscovery;
#[cfg(feature = "usubscription")]
pub use usubscription_client::RpcClientUSubscription;
use crate::{
umessage::{self, UMessageError},
UCode, UMessage, UMessageBuilder, UPayloadFormat, UPriority, UStatus, UUID,
};
mod default_notifier;
mod default_pubsub;
mod in_memory_rpc_client;
mod in_memory_rpc_server;
mod notification;
#[cfg(feature = "usubscription")]
mod pubsub;
mod rpc;
#[cfg(feature = "udiscovery")]
mod udiscovery_client;
#[cfg(feature = "usubscription")]
mod usubscription_client;
#[derive(Clone, Debug)]
pub enum RegistrationError {
AlreadyExists,
MaxListenersExceeded,
NoSuchListener,
PushDeliveryMethodNotSupported,
InvalidFilter(String),
Unknown(UStatus),
}
impl Display for RegistrationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RegistrationError::AlreadyExists => {
f.write_str("a listener for the given filter criteria already exists")
}
RegistrationError::MaxListenersExceeded => {
f.write_str("maximum number of listeners has been reached")
}
RegistrationError::NoSuchListener => {
f.write_str("no listener registered for given pattern")
}
RegistrationError::PushDeliveryMethodNotSupported => f.write_str(
"the underlying transport implementation does not support the push delivery method",
),
RegistrationError::InvalidFilter(msg) => {
f.write_fmt(format_args!("invalid filter(s): {msg}"))
}
RegistrationError::Unknown(status) => f.write_fmt(format_args!(
"error un-/registering listener: {}",
status.get_message()
)),
}
}
}
impl Error for RegistrationError {}
impl From<UStatus> for RegistrationError {
fn from(value: UStatus) -> Self {
match value.code.enum_value() {
Ok(UCode::ALREADY_EXISTS) => RegistrationError::AlreadyExists,
Ok(UCode::NOT_FOUND) => RegistrationError::NoSuchListener,
Ok(UCode::RESOURCE_EXHAUSTED) => RegistrationError::MaxListenersExceeded,
Ok(UCode::UNIMPLEMENTED) => RegistrationError::PushDeliveryMethodNotSupported,
Ok(UCode::INVALID_ARGUMENT) => RegistrationError::InvalidFilter(value.get_message()),
_ => RegistrationError::Unknown(value),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CallOptions {
ttl: u32,
message_id: Option<UUID>,
token: Option<String>,
priority: Option<UPriority>,
}
impl CallOptions {
pub fn for_rpc_request(
ttl: u32,
message_id: Option<UUID>,
token: Option<String>,
priority: Option<UPriority>,
) -> Self {
CallOptions {
ttl,
message_id,
token,
priority,
}
}
pub fn for_notification(
ttl: Option<u32>,
message_id: Option<UUID>,
priority: Option<UPriority>,
) -> Self {
CallOptions {
ttl: ttl.unwrap_or(0),
message_id,
token: None,
priority,
}
}
pub fn for_publish(
ttl: Option<u32>,
message_id: Option<UUID>,
priority: Option<UPriority>,
) -> Self {
CallOptions {
ttl: ttl.unwrap_or(0),
message_id,
token: None,
priority,
}
}
pub fn ttl(&self) -> u32 {
self.ttl
}
pub fn message_id(&self) -> Option<UUID> {
self.message_id.clone()
}
pub fn token(&self) -> Option<String> {
self.token.clone()
}
pub fn priority(&self) -> Option<UPriority> {
self.priority
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct UPayload {
payload_format: UPayloadFormat,
payload: Bytes,
}
impl UPayload {
pub fn new<T: Into<Bytes>>(payload: T, payload_format: UPayloadFormat) -> Self {
UPayload {
payload_format,
payload: payload.into(),
}
}
pub fn try_from_protobuf<M>(message: M) -> Result<Self, UMessageError>
where
M: MessageFull,
{
Any::pack(&message)
.and_then(|any| any.write_to_bytes())
.map(|buf| UPayload::new(buf, UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY))
.map_err(UMessageError::DataSerializationError)
}
pub fn payload_format(&self) -> UPayloadFormat {
self.payload_format
}
pub fn payload(self) -> Bytes {
self.payload
}
pub fn extract_protobuf<T: MessageFull + Default>(&self) -> Result<T, UMessageError> {
umessage::deserialize_protobuf_bytes(&self.payload, &self.payload_format)
}
}
pub(crate) fn apply_common_options(
call_options: CallOptions,
message_builder: &mut UMessageBuilder,
) {
message_builder.with_ttl(call_options.ttl);
if let Some(v) = call_options.message_id {
message_builder.with_message_id(v);
}
if let Some(v) = call_options.priority {
message_builder.with_priority(v);
}
}
pub(crate) fn build_message(
message_builder: &mut UMessageBuilder,
payload: Option<UPayload>,
) -> Result<UMessage, UMessageError> {
if let Some(pl) = payload {
let format = pl.payload_format();
message_builder.build_with_payload(pl.payload, format)
} else {
message_builder.build()
}
}