use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct MsgId(pub String);
impl MsgId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for MsgId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct LinkId(pub String);
impl LinkId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for LinkId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LinkMapping {
pub link_id: LinkId,
pub original_url: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OpenEvent {
pub tenant_id: String,
pub msg_id: MsgId,
pub opened_at_ms: i64,
pub ip_hash: Option<String>,
pub ua_hash: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClickEvent {
pub tenant_id: String,
pub msg_id: MsgId,
pub link_id: LinkId,
pub clicked_at_ms: i64,
pub ip_hash: Option<String>,
pub ua_hash: Option<String>,
}
pub const PIXEL_GIF_BYTES: &[u8] = &[
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3B, ];
pub const PIXEL_GIF_CONTENT_TYPE: &str = "image/gif";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pixel_gif_is_well_formed() {
assert!(PIXEL_GIF_BYTES.starts_with(b"GIF89a") || PIXEL_GIF_BYTES.starts_with(b"GIF87a"),);
assert_eq!(PIXEL_GIF_BYTES.last().copied(), Some(0x3B));
assert!(PIXEL_GIF_BYTES.len() < 100);
}
#[test]
fn newtypes_round_trip_serde() {
let m = MsgId::new("msg-1");
let s = serde_json::to_string(&m).unwrap();
assert_eq!(s, "\"msg-1\"");
let m2: MsgId = serde_json::from_str(&s).unwrap();
assert_eq!(m, m2);
let l = LinkId::new("a1");
let s = serde_json::to_string(&l).unwrap();
assert_eq!(s, "\"a1\"");
}
#[test]
fn newtypes_display_unwrapped() {
assert_eq!(MsgId::new("xyz").to_string(), "xyz");
assert_eq!(LinkId::new("abc").to_string(), "abc");
}
}