1use std::fmt::Display;
7use std::str::FromStr;
8
9use crate::crypto::Hash;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
21pub struct EventCursor(u64);
22
23impl EventCursor {
24 #[must_use]
26 pub fn new(id: u64) -> Self {
27 Self(id)
28 }
29
30 #[must_use]
32 pub fn id(&self) -> u64 {
33 self.0
34 }
35}
36
37impl Display for EventCursor {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(f, "{}", self.0)
40 }
41}
42
43impl FromStr for EventCursor {
44 type Err = std::num::ParseIntError;
45
46 fn from_str(s: &str) -> Result<Self, Self::Err> {
47 Ok(EventCursor(s.parse()?))
48 }
49}
50
51impl From<u64> for EventCursor {
52 fn from(id: u64) -> Self {
53 EventCursor(id)
54 }
55}
56
57impl TryFrom<&str> for EventCursor {
58 type Error = std::num::ParseIntError;
59
60 fn try_from(s: &str) -> Result<Self, Self::Error> {
61 s.parse()
62 }
63}
64
65impl TryFrom<String> for EventCursor {
66 type Error = std::num::ParseIntError;
67
68 fn try_from(s: String) -> Result<Self, Self::Error> {
69 s.parse()
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum EventType {
76 Put {
78 content_hash: Hash,
80 },
81 Delete,
83}
84
85impl EventType {
86 pub fn as_str(&self) -> &'static str {
88 match self {
89 EventType::Put { .. } => "PUT",
90 EventType::Delete => "DEL",
91 }
92 }
93
94 pub fn content_hash(&self) -> Option<&Hash> {
96 match self {
97 EventType::Put { content_hash } => Some(content_hash),
98 EventType::Delete => None,
99 }
100 }
101}
102
103impl Display for EventType {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 write!(f, "{}", self.as_str())
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn cursor_display_and_from_str() {
115 let cursor = EventCursor::new(12345);
116 assert_eq!(cursor.to_string(), "12345");
117
118 let parsed: EventCursor = "67890".parse().unwrap();
119 assert_eq!(parsed.id(), 67890);
120
121 let from_u64: EventCursor = 111u64.into();
122 assert_eq!(from_u64.id(), 111);
123
124 let try_from_str = EventCursor::try_from("222").unwrap();
125 assert_eq!(try_from_str.id(), 222);
126
127 let try_from_string = EventCursor::try_from("333".to_string()).unwrap();
128 assert_eq!(try_from_string.id(), 333);
129 }
130
131 #[test]
132 fn cursor_ordering() {
133 let c1 = EventCursor::new(1);
134 let c2 = EventCursor::new(2);
135 let c3 = EventCursor::new(2);
136
137 assert!(c1 < c2);
138 assert!(c2 > c1);
139 assert_eq!(c2, c3);
140 }
141
142 #[test]
143 fn event_type_display() {
144 let put = EventType::Put {
145 content_hash: Hash::from_bytes([0; 32]),
146 };
147 let del = EventType::Delete;
148
149 assert_eq!(put.to_string(), "PUT");
150 assert_eq!(del.to_string(), "DEL");
151 assert_eq!(put.as_str(), "PUT");
152 assert_eq!(del.as_str(), "DEL");
153 }
154
155 #[test]
156 fn event_type_content_hash() {
157 let hash = Hash::from_bytes([1; 32]);
158 let put = EventType::Put { content_hash: hash };
159 let del = EventType::Delete;
160
161 assert_eq!(put.content_hash(), Some(&hash));
162 assert_eq!(del.content_hash(), None);
163 }
164
165 #[test]
166 fn cursor_parse_error() {
167 assert!("abc".parse::<EventCursor>().is_err());
168 assert!("".parse::<EventCursor>().is_err());
169 assert!("-1".parse::<EventCursor>().is_err());
170 assert!("12.34".parse::<EventCursor>().is_err());
171 }
172}