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}