use std::fmt::Display;
use std::str::FromStr;
use crate::crypto::Hash;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct EventCursor(u64);
impl EventCursor {
#[must_use]
pub fn new(id: u64) -> Self {
Self(id)
}
#[must_use]
pub fn id(&self) -> u64 {
self.0
}
}
impl Display for EventCursor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromStr for EventCursor {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(EventCursor(s.parse()?))
}
}
impl From<u64> for EventCursor {
fn from(id: u64) -> Self {
EventCursor(id)
}
}
impl TryFrom<&str> for EventCursor {
type Error = std::num::ParseIntError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl TryFrom<String> for EventCursor {
type Error = std::num::ParseIntError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EventType {
Put {
content_hash: Hash,
},
Delete,
}
impl EventType {
pub fn as_str(&self) -> &'static str {
match self {
EventType::Put { .. } => "PUT",
EventType::Delete => "DEL",
}
}
pub fn content_hash(&self) -> Option<&Hash> {
match self {
EventType::Put { content_hash } => Some(content_hash),
EventType::Delete => None,
}
}
}
impl Display for EventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cursor_display_and_from_str() {
let cursor = EventCursor::new(12345);
assert_eq!(cursor.to_string(), "12345");
let parsed: EventCursor = "67890".parse().unwrap();
assert_eq!(parsed.id(), 67890);
let from_u64: EventCursor = 111u64.into();
assert_eq!(from_u64.id(), 111);
let try_from_str = EventCursor::try_from("222").unwrap();
assert_eq!(try_from_str.id(), 222);
let try_from_string = EventCursor::try_from("333".to_string()).unwrap();
assert_eq!(try_from_string.id(), 333);
}
#[test]
fn cursor_ordering() {
let c1 = EventCursor::new(1);
let c2 = EventCursor::new(2);
let c3 = EventCursor::new(2);
assert!(c1 < c2);
assert!(c2 > c1);
assert_eq!(c2, c3);
}
#[test]
fn event_type_display() {
let put = EventType::Put {
content_hash: Hash::from_bytes([0; 32]),
};
let del = EventType::Delete;
assert_eq!(put.to_string(), "PUT");
assert_eq!(del.to_string(), "DEL");
assert_eq!(put.as_str(), "PUT");
assert_eq!(del.as_str(), "DEL");
}
#[test]
fn event_type_content_hash() {
let hash = Hash::from_bytes([1; 32]);
let put = EventType::Put {
content_hash: hash.clone(),
};
let del = EventType::Delete;
assert_eq!(put.content_hash(), Some(&hash));
assert_eq!(del.content_hash(), None);
}
#[test]
fn cursor_parse_error() {
assert!("abc".parse::<EventCursor>().is_err());
assert!("".parse::<EventCursor>().is_err());
assert!("-1".parse::<EventCursor>().is_err());
assert!("12.34".parse::<EventCursor>().is_err());
}
}