#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use core::fmt;
macro_rules! string_newtype {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct $name(String);
impl $name {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl AsRef<str> for $name {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<String> for $name {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl From<&str> for $name {
fn from(value: &str) -> Self {
Self::new(value)
}
}
impl fmt::Display for $name {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
};
}
string_newtype! {
ChangeEventId
}
string_newtype! {
ChangeCursor
}
string_newtype! {
ResumeToken
}
string_newtype! {
ChangedDocument
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ChangeSequence(u64);
impl ChangeSequence {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn value(self) -> u64 {
self.0
}
}
impl fmt::Display for ChangeSequence {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.0)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ChangeEventKind {
Insert,
Update,
Delete,
Replace,
Upsert,
Expire,
#[default]
Unknown,
}
impl ChangeEventKind {
pub const fn as_str(self) -> &'static str {
match self {
Self::Insert => "insert",
Self::Update => "update",
Self::Delete => "delete",
Self::Replace => "replace",
Self::Upsert => "upsert",
Self::Expire => "expire",
Self::Unknown => "unknown",
}
}
}
impl fmt::Display for ChangeEventKind {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ChangeEvent {
id: ChangeEventId,
kind: ChangeEventKind,
document: ChangedDocument,
cursor: Option<ChangeCursor>,
resume_token: Option<ResumeToken>,
sequence: Option<ChangeSequence>,
}
impl ChangeEvent {
pub fn new(id: ChangeEventId, kind: ChangeEventKind, document: impl Into<String>) -> Self {
Self {
id,
kind,
document: ChangedDocument::new(document),
cursor: None,
resume_token: None,
sequence: None,
}
}
pub fn with_cursor(mut self, cursor: ChangeCursor) -> Self {
self.cursor = Some(cursor);
self
}
pub fn with_resume_token(mut self, resume_token: ResumeToken) -> Self {
self.resume_token = Some(resume_token);
self
}
pub const fn with_sequence(mut self, sequence: ChangeSequence) -> Self {
self.sequence = Some(sequence);
self
}
pub const fn id(&self) -> &ChangeEventId {
&self.id
}
pub const fn kind(&self) -> ChangeEventKind {
self.kind
}
pub const fn document(&self) -> &ChangedDocument {
&self.document
}
pub const fn cursor(&self) -> Option<&ChangeCursor> {
self.cursor.as_ref()
}
pub const fn resume_token(&self) -> Option<&ResumeToken> {
self.resume_token.as_ref()
}
pub const fn sequence(&self) -> Option<ChangeSequence> {
self.sequence
}
}
#[cfg(test)]
mod tests {
use super::{
ChangeCursor, ChangeEvent, ChangeEventId, ChangeEventKind, ChangeSequence, ResumeToken,
};
#[test]
fn formats_change_event_kinds() {
assert_eq!(ChangeEventKind::Insert.to_string(), "insert");
assert_eq!(ChangeEventKind::Update.to_string(), "update");
assert_eq!(ChangeEventKind::Delete.to_string(), "delete");
assert_eq!(ChangeEventKind::Unknown.to_string(), "unknown");
}
#[test]
fn builds_change_events() {
let event = ChangeEvent::new(
ChangeEventId::new("evt_1"),
ChangeEventKind::Replace,
"customer_123",
)
.with_cursor(ChangeCursor::new("cursor_1"))
.with_resume_token(ResumeToken::new("token_1"))
.with_sequence(ChangeSequence::new(42));
assert_eq!(event.id().as_str(), "evt_1");
assert_eq!(event.kind(), ChangeEventKind::Replace);
assert_eq!(event.document().as_ref(), "customer_123");
assert_eq!(event.sequence(), Some(ChangeSequence::new(42)));
}
}