Skip to main content

origin_platform/
notifications.rs

1use async_trait::async_trait;
2use origin_domain::Result;
3use serde::{Deserialize, Serialize};
4use std::fmt::Debug;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum Urgency {
9    Low,
10    #[default]
11    Normal,
12    Critical,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct Notification {
17    pub title: String,
18    pub body: Option<String>,
19    pub urgency: Urgency,
20    /// Groups related notifications so a repeat replaces the previous one instead of
21    /// stacking. Usually the alert fingerprint.
22    pub tag: Option<String>,
23}
24
25impl Notification {
26    pub fn new(title: impl Into<String>) -> Self {
27        Self {
28            title: title.into(),
29            body: None,
30            urgency: Urgency::Normal,
31            tag: None,
32        }
33    }
34
35    pub fn with_body(mut self, body: impl Into<String>) -> Self {
36        self.body = Some(body.into());
37        self
38    }
39
40    pub fn with_urgency(mut self, urgency: Urgency) -> Self {
41        self.urgency = urgency;
42        self
43    }
44
45    pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
46        self.tag = Some(tag.into());
47        self
48    }
49}
50
51/// Native user notifications.
52///
53/// Implementations must not fail the caller when the user has denied notification
54/// permission — a suppressed notification is a normal outcome, not an error.
55#[async_trait]
56pub trait NotificationService: Debug + Send + Sync + 'static {
57    async fn notify(&self, notification: Notification) -> Result<()>;
58}
59
60/// Drops every notification. Used for headless runs and CLI builds.
61#[derive(Debug, Clone, Copy, Default)]
62pub struct NoopNotificationService;
63
64#[async_trait]
65impl NotificationService for NoopNotificationService {
66    async fn notify(&self, notification: Notification) -> Result<()> {
67        tracing::debug!(title = %notification.title, "notification dropped (noop service)");
68        Ok(())
69    }
70}