Skip to main content

clash_notify/
lib.rs

1use std::time::Duration;
2
3#[cfg(target_os = "linux")]
4mod linux;
5#[cfg(target_os = "macos")]
6mod macos;
7
8/// Response from an interactive desktop notification prompt.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum PromptResponse {
11    Approved,
12    Denied,
13    TimedOut,
14    /// Platform doesn't support interactive notifications.
15    Unavailable,
16}
17
18/// One-time initialization. On macOS, sets the bundle identifier used for
19/// notifications. Safe to call multiple times (subsequent calls are no-ops).
20pub fn init() {
21    #[cfg(target_os = "macos")]
22    macos::init();
23}
24
25/// Fire-and-forget desktop notification. Errors are logged but never propagated.
26pub fn notify(title: &str, body: &str) {
27    #[cfg(target_os = "macos")]
28    macos::notify(title, body);
29
30    #[cfg(target_os = "linux")]
31    linux::notify(title, body);
32
33    #[cfg(not(any(target_os = "macos", target_os = "linux")))]
34    {
35        let _ = (title, body);
36        tracing::warn!("Desktop notifications not supported on this platform");
37    }
38}
39
40/// Interactive notification with Approve/Deny buttons.
41///
42/// Blocks until the user responds or the timeout elapses. A thread-level
43/// timeout guard ensures we never block forever even if the notification
44/// daemon is unresponsive.
45pub fn prompt(title: &str, body: &str, timeout: Duration) -> PromptResponse {
46    #[cfg(target_os = "macos")]
47    {
48        macos::prompt(title, body, timeout)
49    }
50
51    #[cfg(target_os = "linux")]
52    {
53        linux::prompt(title, body, timeout)
54    }
55
56    #[cfg(not(any(target_os = "macos", target_os = "linux")))]
57    {
58        let _ = (title, body, timeout);
59        PromptResponse::Unavailable
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn prompt_response_debug() {
69        // Ensure the enum derives work.
70        assert_eq!(format!("{:?}", PromptResponse::Approved), "Approved");
71        assert_eq!(format!("{:?}", PromptResponse::Denied), "Denied");
72        assert_eq!(format!("{:?}", PromptResponse::TimedOut), "TimedOut");
73        assert_eq!(format!("{:?}", PromptResponse::Unavailable), "Unavailable");
74    }
75
76    #[test]
77    fn prompt_response_eq() {
78        assert_eq!(PromptResponse::Approved, PromptResponse::Approved);
79        assert_ne!(PromptResponse::Approved, PromptResponse::Denied);
80    }
81}