Skip to content

Commit

Permalink
Support enabling multiple notification sinks (#344)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nemo157 authored Sep 17, 2024
1 parent e40a8a8 commit 9a9bdb4
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 18 deletions.
2 changes: 2 additions & 0 deletions docs/iamb.5
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ to use the desktop mechanism (default).
Setting this field to
.Dq Sy bell
will use the terminal bell instead.
Both can be used via
.Dq Sy desktop|bell .

.It Sy show_message
controls whether to show the message in the desktop notification, and defaults to
Expand Down
88 changes: 79 additions & 9 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,23 +398,70 @@ pub enum UserDisplayStyle {
DisplayName,
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum NotifyVia {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct NotifyVia {
/// Deliver notifications via terminal bell.
Bell,
pub bell: bool,
/// Deliver notifications via desktop mechanism.
#[cfg(feature = "desktop")]
Desktop,
pub desktop: bool,
}
pub struct NotifyViaVisitor;

impl Default for NotifyVia {
fn default() -> Self {
#[cfg(not(feature = "desktop"))]
return NotifyVia::Bell;
Self {
bell: cfg!(not(feature = "desktop")),
#[cfg(feature = "desktop")]
desktop: true,
}
}
}

impl<'de> Visitor<'de> for NotifyViaVisitor {
type Value = NotifyVia;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid notify destination (e.g. \"bell\" or \"desktop\")")
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: SerdeError,
{
let mut via = NotifyVia {
bell: false,
#[cfg(feature = "desktop")]
desktop: false,
};

#[cfg(feature = "desktop")]
return NotifyVia::Desktop;
for value in value.split('|') {
match value.to_ascii_lowercase().as_str() {
"bell" => {
via.bell = true;
},
#[cfg(feature = "desktop")]
"desktop" => {
via.desktop = true;
},
#[cfg(not(feature = "desktop"))]
"desktop" => {
return Err(E::custom("desktop notification support was compiled out"))
},
_ => return Err(E::custom("could not parse into a notify destination")),
};
}

Ok(via)
}
}

impl<'de> Deserialize<'de> for NotifyVia {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(NotifyViaVisitor)
}
}

Expand Down Expand Up @@ -1189,6 +1236,29 @@ mod tests {
assert_eq!(run, &exp);
}

#[test]
fn test_parse_notify_via() {
assert_eq!(NotifyVia { bell: false, desktop: true }, NotifyVia::default());
assert_eq!(
NotifyVia { bell: false, desktop: true },
serde_json::from_str(r#""desktop""#).unwrap()
);
assert_eq!(
NotifyVia { bell: true, desktop: false },
serde_json::from_str(r#""bell""#).unwrap()
);
assert_eq!(
NotifyVia { bell: true, desktop: true },
serde_json::from_str(r#""bell|desktop""#).unwrap()
);
assert_eq!(
NotifyVia { bell: true, desktop: true },
serde_json::from_str(r#""desktop|bell""#).unwrap()
);
assert!(serde_json::from_str::<NotifyVia>(r#""other""#).is_err());
assert!(serde_json::from_str::<NotifyVia>(r#""""#).is_err());
}

#[test]
fn test_load_example_config_toml() {
let path = PathBuf::from("config.example.toml");
Expand Down
32 changes: 24 additions & 8 deletions src/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ pub async fn register_notifications(
return;
}

match notify_via {
#[cfg(feature = "desktop")]
NotifyVia::Desktop => send_notification_desktop(summary, body),
NotifyVia::Bell => send_notification_bell(&store).await,
}
send_notification(&notify_via, &store, &summary, body.as_deref()).await;
},
Err(err) => {
tracing::error!("Failed to extract notification data: {err}")
Expand All @@ -78,22 +74,42 @@ pub async fn register_notifications(
.await;
}

async fn send_notification(
via: &NotifyVia,
store: &AsyncProgramStore,
summary: &str,
body: Option<&str>,
) {
#[cfg(feature = "desktop")]
if via.desktop {
send_notification_desktop(summary, body);
}
#[cfg(not(feature = "desktop"))]
{
let _ = (summary, body, IAMB_XDG_NAME);
}

if via.bell {
send_notification_bell(store).await;
}
}

async fn send_notification_bell(store: &AsyncProgramStore) {
let mut locked = store.lock().await;
locked.application.ring_bell = true;
}

#[cfg(feature = "desktop")]
fn send_notification_desktop(summary: String, body: Option<String>) {
fn send_notification_desktop(summary: &str, body: Option<&str>) {
let mut desktop_notification = notify_rust::Notification::new();
desktop_notification
.summary(&summary)
.summary(summary)
.appname(IAMB_XDG_NAME)
.icon(IAMB_XDG_NAME)
.action("default", "default");

if let Some(body) = body {
desktop_notification.body(&body);
desktop_notification.body(body);
}

if let Err(err) = desktop_notification.show() {
Expand Down
2 changes: 1 addition & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ pub fn mock_tunables() -> TunableValues {
message_user_color: false,
notifications: Notifications {
enabled: false,
via: NotifyVia::Desktop,
via: NotifyVia::default(),
show_message: true,
},
image_preview: None,
Expand Down

0 comments on commit 9a9bdb4

Please sign in to comment.