Skip to main content

notify_rust/
response.rs

1//! Cross-platform notification response types.
2//!
3//! These types describe the outcome of a shown notification — whether the
4//! user interacted with it or it was closed by the platform.
5//! They are shared between all backends so consumer code does not need
6//! a `cfg` switch to read responses.
7
8/// Reason a notification was closed without an action being invoked.
9///
10/// ### Platform notes
11///
12/// **XDG (Linux/BSD):** maps directly to `NotificationClosed` D-Bus signal reasons.
13///
14/// **macOS:** the system does not distinguish close reasons, so all closes are
15/// reported as [`CloseReason::Dismissed`].
16///
17/// **Windows:** `UserCanceled` → [`Dismissed`](CloseReason::Dismissed),
18/// `TimedOut` → [`Expired`](CloseReason::Expired),
19/// `ApplicationHidden` → [`CloseAction`](CloseReason::CloseAction).
20// #[non_exhaustive] // TODO: mark in 5.0
21#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
22pub enum CloseReason {
23    /// The notification expired (timed out).
24    Expired,
25
26    /// The notification was dismissed by the user.
27    Dismissed,
28
29    /// The notification was closed programmatically.
30    CloseAction,
31
32    /// An unrecognised or reserved reason was reported by the platform.
33    Other(u32),
34}
35
36impl From<u32> for CloseReason {
37    fn from(raw_reason: u32) -> Self {
38        match raw_reason {
39            1 => CloseReason::Expired,
40            2 => CloseReason::Dismissed,
41            3 => CloseReason::CloseAction,
42            other => CloseReason::Other(other),
43        }
44    }
45}
46
47/// The outcome of a shown notification.
48///
49/// Returned by [`wait_for_response`](crate::NotificationHandle::wait_for_response).
50///
51/// Match on this to handle every possible outcome:
52///
53/// ```no_run
54/// # use notify_rust::{NotificationResponse, CloseReason};
55/// # let response = NotificationResponse::Closed(CloseReason::Dismissed);
56/// match response {
57///     NotificationResponse::Default => println!("body clicked"),
58///     NotificationResponse::Action(ref key) => println!("button '{key}' clicked"),
59///     NotificationResponse::Reply(ref text) => println!("user replied: {text}"),
60///     NotificationResponse::Closed(reason) => println!("closed: {reason:?}"),
61/// }
62/// ```
63#[derive(Clone, Debug, PartialEq, Eq, Hash)]
64pub enum NotificationResponse {
65    /// The default action was invoked — the user activated the notification without
66    /// choosing a specific button (e.g. clicked the body, tapped the banner).
67    ///
68    /// Corresponds to the D-Bus `"default"` action key,
69    /// Apple's `UNNotificationDefaultActionIdentifier`, and a body-click on Windows.
70    Default,
71
72    /// The user invoked a named action button.
73    Action(String),
74
75    /// The user submitted an inline text reply.
76    ///
77    /// Only produced by the `preview-macos-un` backend (macOS `UNUserNotificationCenter`
78    /// with an inline reply action). On all other backends this variant is never emitted.
79    Reply(String),
80
81    /// The notification was closed without any action being taken.
82    Closed(CloseReason),
83}
84
85impl NotificationResponse {
86    /// Returns `true` if this response is the [`Default`](NotificationResponse::Default) variant.
87    pub fn is_default_action(&self) -> bool {
88        matches!(self, NotificationResponse::Default)
89    }
90}
91
92impl From<String> for NotificationResponse {
93    fn from(key: String) -> Self {
94        Self::Action(key)
95    }
96}
97
98impl From<&str> for NotificationResponse {
99    fn from(key: &str) -> Self {
100        Self::Action(key.to_owned())
101    }
102}
103
104/// Response to an action, a backward-compatible facade.
105///
106/// This type is preserved for source compatibility with existing match arms and type signatures.
107/// Prefer [`NotificationResponse`] for new code, which owns its data and covers more cases.
108///
109/// **Deprecated since 4.18.0** — use [`NotificationResponse`] instead.
110#[derive(Clone, Debug)]
111pub enum ActionResponse<'a> {
112    /// The user clicked a named action button (or the notification body, key `"default"`).
113    Custom(&'a str),
114    /// The notification was closed without any action being taken.
115    Closed(CloseReason),
116}
117
118impl<'a> From<&'a str> for ActionResponse<'a> {
119    fn from(raw: &'a str) -> Self {
120        Self::Custom(raw)
121    }
122}
123
124/// Helper trait implemented by closures used with [`NotificationHandle::wait_for_response`](crate::NotificationHandle::wait_for_response).
125///
126/// Any `FnOnce(&NotificationResponse)` closure automatically implements this trait.
127pub trait ResponseHandler {
128    /// Invoke the handler with the given response.
129    fn call(self, response: &NotificationResponse);
130}
131
132impl<F> ResponseHandler for F
133where
134    F: FnOnce(&NotificationResponse),
135{
136    fn call(self, response: &NotificationResponse) {
137        (self)(response);
138    }
139}
140
141/// Callback for the close signal of a notification.
142///
143/// Implemented for both `Fn(CloseReason)` and `Fn()`, so there is rarely
144/// a good reason to implement this manually.
145pub trait CloseHandler<T> {
146    /// Called with the [`CloseReason`].
147    fn call(&self, reason: CloseReason);
148}
149
150impl<F> CloseHandler<CloseReason> for F
151where
152    F: Fn(CloseReason),
153{
154    fn call(&self, reason: CloseReason) {
155        self(reason);
156    }
157}
158
159impl<F> CloseHandler<()> for F
160where
161    F: Fn(),
162{
163    fn call(&self, _reason: CloseReason) {
164        self();
165    }
166}