use std::time::SystemTime;
use super::RepositoryError;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InboxReceipt {
pub consumer: String,
pub message_id: String,
pub processed_at: SystemTime,
}
impl InboxReceipt {
pub fn new(consumer: impl Into<String>, message_id: impl Into<String>) -> Self {
Self {
consumer: consumer.into(),
message_id: message_id.into(),
processed_at: SystemTime::now(),
}
}
pub fn key(&self) -> (&str, &str) {
(&self.consumer, &self.message_id)
}
pub fn validate(&self) -> Result<(), RepositoryError> {
if self.consumer.is_empty() || self.message_id.is_empty() {
return Err(RepositoryError::InvalidInboxReceipt {
consumer: self.consumer.clone(),
message_id: self.message_id.clone(),
});
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InboxOutcome {
Processed,
Duplicate,
}
impl InboxOutcome {
pub fn is_processed(self) -> bool {
matches!(self, InboxOutcome::Processed)
}
pub fn is_duplicate(self) -> bool {
matches!(self, InboxOutcome::Duplicate)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn receipt_key_is_consumer_and_message_id() {
let r = InboxReceipt::new("projections", "evt-1");
assert_eq!(r.key(), ("projections", "evt-1"));
assert_eq!(r.consumer, "projections");
assert_eq!(r.message_id, "evt-1");
}
#[test]
fn outcome_predicates() {
assert!(InboxOutcome::Processed.is_processed());
assert!(!InboxOutcome::Processed.is_duplicate());
assert!(InboxOutcome::Duplicate.is_duplicate());
assert!(!InboxOutcome::Duplicate.is_processed());
}
#[test]
fn validate_rejects_empty_fields() {
assert!(InboxReceipt::new("c", "m").validate().is_ok());
assert!(matches!(
InboxReceipt::new("", "m").validate(),
Err(RepositoryError::InvalidInboxReceipt { .. })
));
assert!(matches!(
InboxReceipt::new("c", "").validate(),
Err(RepositoryError::InvalidInboxReceipt { .. })
));
}
}