Skip to main content

anvil_core/
notification.rs

1//! Notifications. A notifiable can receive a notification via one or more channels.
2
3use async_trait::async_trait;
4
5use crate::container::Container;
6use crate::mail::OutgoingMessage;
7use crate::Error;
8
9#[derive(Debug, Clone)]
10pub enum Channel {
11    Mail,
12    Database,
13    Slack,
14}
15
16#[async_trait]
17pub trait Notification: Send + Sync {
18    fn channels(&self) -> Vec<Channel>;
19
20    async fn to_mail(&self, _container: &Container) -> Result<Option<OutgoingMessage>, Error> {
21        Ok(None)
22    }
23
24    async fn to_database(&self, _container: &Container) -> Result<Option<serde_json::Value>, Error> {
25        Ok(None)
26    }
27
28    async fn to_slack(&self, _container: &Container) -> Result<Option<SlackMessage>, Error> {
29        Ok(None)
30    }
31}
32
33#[derive(Debug, Clone, serde::Serialize)]
34pub struct SlackMessage {
35    pub webhook: String,
36    pub text: String,
37}
38
39pub async fn notify<N: Notification>(
40    container: &Container,
41    notification: &N,
42) -> Result<(), Error> {
43    for channel in notification.channels() {
44        match channel {
45            Channel::Mail => {
46                if let Some(msg) = notification.to_mail(container).await? {
47                    container.mailer().send(msg).await?;
48                }
49            }
50            Channel::Database => {
51                if let Some(payload) = notification.to_database(container).await? {
52                    tracing::info!(?payload, "notification stored in database channel (POC stub)");
53                }
54            }
55            Channel::Slack => {
56                if let Some(slack) = notification.to_slack(container).await? {
57                    tracing::info!(?slack, "notification posted to slack channel (POC stub)");
58                }
59            }
60        }
61    }
62    Ok(())
63}