1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5
6macro_rules! string_newtype {
7 ($(#[$meta:meta])* $name:ident) => {
8 $(#[$meta])*
9 #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
10 pub struct $name(String);
11
12 impl $name {
13 pub fn new(value: impl Into<String>) -> Self {
15 Self(value.into())
16 }
17
18 pub fn as_str(&self) -> &str {
20 &self.0
21 }
22 }
23
24 impl AsRef<str> for $name {
25 fn as_ref(&self) -> &str {
26 self.as_str()
27 }
28 }
29
30 impl From<String> for $name {
31 fn from(value: String) -> Self {
32 Self::new(value)
33 }
34 }
35
36 impl From<&str> for $name {
37 fn from(value: &str) -> Self {
38 Self::new(value)
39 }
40 }
41
42 impl fmt::Display for $name {
43 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44 formatter.write_str(self.as_str())
45 }
46 }
47 };
48}
49
50string_newtype! {
51 ChangeEventId
53}
54string_newtype! {
55 ChangeCursor
57}
58string_newtype! {
59 ResumeToken
61}
62string_newtype! {
63 ChangedDocument
65}
66
67#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
69pub struct ChangeSequence(u64);
70
71impl ChangeSequence {
72 pub const fn new(value: u64) -> Self {
74 Self(value)
75 }
76
77 pub const fn value(self) -> u64 {
79 self.0
80 }
81}
82
83impl fmt::Display for ChangeSequence {
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 write!(formatter, "{}", self.0)
86 }
87}
88
89#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
91pub enum ChangeEventKind {
92 Insert,
93 Update,
94 Delete,
95 Replace,
96 Upsert,
97 Expire,
98 #[default]
99 Unknown,
100}
101
102impl ChangeEventKind {
103 pub const fn as_str(self) -> &'static str {
105 match self {
106 Self::Insert => "insert",
107 Self::Update => "update",
108 Self::Delete => "delete",
109 Self::Replace => "replace",
110 Self::Upsert => "upsert",
111 Self::Expire => "expire",
112 Self::Unknown => "unknown",
113 }
114 }
115}
116
117impl fmt::Display for ChangeEventKind {
118 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
119 formatter.write_str(self.as_str())
120 }
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
125pub struct ChangeEvent {
126 id: ChangeEventId,
127 kind: ChangeEventKind,
128 document: ChangedDocument,
129 cursor: Option<ChangeCursor>,
130 resume_token: Option<ResumeToken>,
131 sequence: Option<ChangeSequence>,
132}
133
134impl ChangeEvent {
135 pub fn new(id: ChangeEventId, kind: ChangeEventKind, document: impl Into<String>) -> Self {
137 Self {
138 id,
139 kind,
140 document: ChangedDocument::new(document),
141 cursor: None,
142 resume_token: None,
143 sequence: None,
144 }
145 }
146
147 pub fn with_cursor(mut self, cursor: ChangeCursor) -> Self {
149 self.cursor = Some(cursor);
150 self
151 }
152
153 pub fn with_resume_token(mut self, resume_token: ResumeToken) -> Self {
155 self.resume_token = Some(resume_token);
156 self
157 }
158
159 pub const fn with_sequence(mut self, sequence: ChangeSequence) -> Self {
161 self.sequence = Some(sequence);
162 self
163 }
164
165 pub const fn id(&self) -> &ChangeEventId {
167 &self.id
168 }
169
170 pub const fn kind(&self) -> ChangeEventKind {
172 self.kind
173 }
174
175 pub const fn document(&self) -> &ChangedDocument {
177 &self.document
178 }
179
180 pub const fn cursor(&self) -> Option<&ChangeCursor> {
182 self.cursor.as_ref()
183 }
184
185 pub const fn resume_token(&self) -> Option<&ResumeToken> {
187 self.resume_token.as_ref()
188 }
189
190 pub const fn sequence(&self) -> Option<ChangeSequence> {
192 self.sequence
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::{
199 ChangeCursor, ChangeEvent, ChangeEventId, ChangeEventKind, ChangeSequence, ResumeToken,
200 };
201
202 #[test]
203 fn formats_change_event_kinds() {
204 assert_eq!(ChangeEventKind::Insert.to_string(), "insert");
205 assert_eq!(ChangeEventKind::Update.to_string(), "update");
206 assert_eq!(ChangeEventKind::Delete.to_string(), "delete");
207 assert_eq!(ChangeEventKind::Unknown.to_string(), "unknown");
208 }
209
210 #[test]
211 fn builds_change_events() {
212 let event = ChangeEvent::new(
213 ChangeEventId::new("evt_1"),
214 ChangeEventKind::Replace,
215 "customer_123",
216 )
217 .with_cursor(ChangeCursor::new("cursor_1"))
218 .with_resume_token(ResumeToken::new("token_1"))
219 .with_sequence(ChangeSequence::new(42));
220
221 assert_eq!(event.id().as_str(), "evt_1");
222 assert_eq!(event.kind(), ChangeEventKind::Replace);
223 assert_eq!(event.document().as_ref(), "customer_123");
224 assert_eq!(event.sequence(), Some(ChangeSequence::new(42)));
225 }
226}