#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
pub enum MessageKind {
Command,
Event,
}
impl MessageKind {
pub fn as_str(&self) -> &'static str {
match self {
MessageKind::Command => "command",
MessageKind::Event => "event",
}
}
pub fn from_str_lossy(value: &str) -> MessageKind {
match value {
"command" => MessageKind::Command,
_ => MessageKind::Event,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SubscriptionPlan {
pub commands: Vec<String>,
pub events: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PayloadDecodeError(pub String);
impl std::fmt::Display for PayloadDecodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for PayloadDecodeError {}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Message {
pub id: Option<String>,
pub name: String,
pub kind: MessageKind,
pub payload: Vec<u8>,
pub content_type: String,
pub metadata: Vec<(String, String)>,
}
impl Message {
pub fn new(name: impl Into<String>, kind: MessageKind, payload: Vec<u8>) -> Self {
Self {
id: None,
name: name.into(),
kind,
payload,
content_type: "application/json".to_string(),
metadata: Vec::new(),
}
}
pub fn with_id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.push((key.into(), value.into()));
self
}
pub fn id(&self) -> Option<&str> {
self.id.as_deref()
}
pub fn name(&self) -> &str {
&self.name
}
pub fn validate_name(&self) -> Result<(), super::MessageNameError> {
super::validate_message_name(&self.name).map(|_| ())
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn metadata(&self, key: &str) -> Option<&str> {
self.metadata
.iter()
.find(|(existing, _)| existing.eq_ignore_ascii_case(key))
.map(|(_, value)| value.as_str())
}
pub fn correlation_id(&self) -> Option<&str> {
self.metadata("correlation_id")
}
pub fn causation_id(&self) -> Option<&str> {
self.metadata("causation_id")
}
pub fn payload_json<T: serde::de::DeserializeOwned>(&self) -> Result<T, PayloadDecodeError> {
serde_json::from_slice(&self.payload).map_err(|e| {
PayloadDecodeError(format!(
"invalid JSON payload for message '{}': {}",
self.name, e
))
})
}
pub fn payload_bitcode<T: serde::de::DeserializeOwned>(&self) -> Result<T, PayloadDecodeError> {
bitcode::deserialize(&self.payload).map_err(|e| {
PayloadDecodeError(format!(
"invalid bitcode payload for message '{}': {}",
self.name, e
))
})
}
}
#[cfg(any(feature = "nats", feature = "kafka", feature = "rabbitmq"))]
pub(crate) fn strip_address_prefix(address: String, prefix: Option<&str>) -> String {
match prefix {
Some(prefix) => address
.strip_prefix(prefix)
.map(str::to_string)
.unwrap_or(address),
None => address,
}
}