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(
25        &self,
26        _container: &Container,
27    ) -> Result<Option<serde_json::Value>, Error> {
28        Ok(None)
29    }
30
31    async fn to_slack(&self, _container: &Container) -> Result<Option<SlackMessage>, Error> {
32        Ok(None)
33    }
34}
35
36#[derive(Debug, Clone, serde::Serialize)]
37pub struct SlackMessage {
38    pub webhook: String,
39    pub text: String,
40}
41
42pub async fn notify<N: Notification>(container: &Container, notification: &N) -> 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!(
53                        ?payload,
54                        "notification stored in database channel (POC stub)"
55                    );
56                }
57            }
58            Channel::Slack => {
59                if let Some(slack) = notification.to_slack(container).await? {
60                    tracing::info!(?slack, "notification posted to slack channel (POC stub)");
61                }
62            }
63        }
64    }
65    Ok(())
66}