#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReplayId(pub(crate) Vec<u8>);
impl ReplayId {
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self(bytes)
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct EventMessage<T> {
pub payload: T,
pub replay_id: ReplayId,
pub schema_id: String,
pub event_id: String,
}
#[derive(Debug, Clone)]
pub enum PubSubEvent<T> {
Event(EventMessage<T>),
Reconnected {
replay_id: ReplayId,
attempt: u32,
},
KeepAlive,
}
#[derive(Debug, Clone)]
pub struct PublishResult {
pub replay_id: Option<ReplayId>,
pub error: Option<String>,
}
impl PublishResult {
#[must_use]
pub const fn is_success(&self) -> bool {
self.error.is_none()
}
}
#[derive(Debug, Clone)]
pub struct PublishResponse {
pub topic_name: String,
pub results: Vec<PublishResult>,
}
impl PublishResponse {
#[must_use]
pub fn all_succeeded(&self) -> bool {
self.results.iter().all(PublishResult::is_success)
}
#[must_use]
pub fn failure_count(&self) -> usize {
self.results.iter().filter(|r| !r.is_success()).count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_replay_id_from_bytes_roundtrip() {
let bytes = vec![1u8, 2, 3, 4];
let id = ReplayId::from_bytes(bytes.clone());
assert_eq!(id.as_bytes(), bytes.as_slice());
}
#[test]
fn test_replay_id_empty() {
let id = ReplayId::from_bytes(vec![]);
assert!(id.is_empty());
}
#[test]
fn test_replay_id_not_empty() {
let id = ReplayId::from_bytes(vec![1, 2, 3]);
assert!(!id.is_empty());
}
#[test]
fn test_publish_result_success() {
let r = PublishResult {
replay_id: Some(ReplayId::from_bytes(vec![1])),
error: None,
};
assert!(r.is_success());
}
#[test]
fn test_publish_result_failure() {
let r = PublishResult {
replay_id: None,
error: Some("INVALID_TYPE".to_string()),
};
assert!(!r.is_success());
}
#[test]
fn test_publish_response_all_succeeded() {
let resp = PublishResponse {
topic_name: "/event/MyEvent__e".to_string(),
results: vec![
PublishResult {
replay_id: Some(ReplayId::from_bytes(vec![1])),
error: None,
},
PublishResult {
replay_id: Some(ReplayId::from_bytes(vec![2])),
error: None,
},
],
};
assert!(resp.all_succeeded());
assert_eq!(resp.failure_count(), 0);
}
#[test]
fn test_publish_response_partial_failure() {
let resp = PublishResponse {
topic_name: "/event/MyEvent__e".to_string(),
results: vec![
PublishResult {
replay_id: Some(ReplayId::from_bytes(vec![1])),
error: None,
},
PublishResult {
replay_id: None,
error: Some("ERR".to_string()),
},
],
};
assert!(!resp.all_succeeded());
assert_eq!(resp.failure_count(), 1);
}
}