use crate::channel::Channel;
use crate::channels::DatabaseMessage;
use crate::dispatcher::NotificationDispatcher;
use crate::notification::Notification;
use crate::Error;
use async_trait::async_trait;
#[async_trait]
pub trait Notifiable: Send + Sync {
fn route_notification_for(&self, channel: Channel) -> Option<String>;
fn notifiable_id(&self) -> String {
"unknown".to_string()
}
fn notifiable_type(&self) -> &'static str {
std::any::type_name::<Self>()
}
async fn notify<N: Notification + 'static>(&self, notification: N) -> Result<(), Error> {
NotificationDispatcher::send(self, notification).await
}
}
#[derive(Debug)]
pub struct ChannelResult {
pub channel: Channel,
pub success: bool,
pub error: Option<String>,
}
impl ChannelResult {
pub fn success(channel: Channel) -> Self {
Self {
channel,
success: true,
error: None,
}
}
pub fn failure(channel: Channel, error: impl Into<String>) -> Self {
Self {
channel,
success: false,
error: Some(error.into()),
}
}
}
#[async_trait]
pub trait DatabaseNotificationStore: Send + Sync {
async fn store(
&self,
notifiable_id: &str,
notifiable_type: &str,
notification_type: &str,
message: &DatabaseMessage,
) -> Result<(), Error>;
async fn mark_as_read(&self, notification_id: &str) -> Result<(), Error>;
async fn unread(&self, notifiable_id: &str) -> Result<Vec<StoredNotification>, Error>;
}
#[derive(Debug, Clone)]
pub struct StoredNotification {
pub id: String,
pub notifiable_id: String,
pub notifiable_type: String,
pub notification_type: String,
pub data: String,
pub read_at: Option<String>,
pub created_at: String,
}
#[cfg(test)]
mod tests {
use super::*;
struct TestUser {
id: i64,
email: String,
}
impl Notifiable for TestUser {
fn route_notification_for(&self, channel: Channel) -> Option<String> {
match channel {
Channel::Mail => Some(self.email.clone()),
Channel::Database => Some(self.id.to_string()),
_ => None,
}
}
fn notifiable_id(&self) -> String {
self.id.to_string()
}
}
#[test]
fn test_route_notification_for() {
let user = TestUser {
id: 42,
email: "test@example.com".to_string(),
};
assert_eq!(
user.route_notification_for(Channel::Mail),
Some("test@example.com".to_string())
);
assert_eq!(
user.route_notification_for(Channel::Database),
Some("42".to_string())
);
assert_eq!(user.route_notification_for(Channel::Slack), None);
}
#[test]
fn test_channel_result() {
let success = ChannelResult::success(Channel::Mail);
assert!(success.success);
assert!(success.error.is_none());
let failure = ChannelResult::failure(Channel::Slack, "Connection failed");
assert!(!failure.success);
assert_eq!(failure.error, Some("Connection failed".to_string()));
}
}