From 1937fbd083aa06b021b9888a13159672188c5c23 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Apr 2026 01:06:22 +0200 Subject: [PATCH] update notify-rust --- Cargo.lock | 172 +++------------------------------------------------- Cargo.toml | 3 +- src/main.rs | 98 +++++++++++++++++------------- 3 files changed, 68 insertions(+), 205 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3858e5..2a41327 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,79 +147,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", -] - [[package]] name = "async-recursion" version = "1.1.1" @@ -231,30 +158,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async-signal" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -266,12 +169,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.5.0" @@ -343,19 +240,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "borsh" version = "1.6.0" @@ -838,6 +722,7 @@ dependencies = [ "ashpd", "clap", "ctrlc", + "futures-util", "hex", "home", "main_error", @@ -955,12 +840,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - [[package]] name = "hex" version = "0.4.3" @@ -1700,9 +1579,9 @@ dependencies = [ [[package]] name = "mac-notification-sys" -version = "0.6.9" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fd3f75411f4725061682ed91f131946e912859d0044d39c4ec0aac818d7621" +checksum = "29a16783dd1a47849b8c8133c9cd3eb2112cfbc6901670af3dba47c8bbfb07d3" dependencies = [ "cc", "objc2", @@ -1815,9 +1694,9 @@ dependencies = [ [[package]] name = "notify-rust" -version = "4.13.0" +version = "4.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9612133a804b4bc753f9f806de73fc9730b7694eb1bada2dc252cce022638be8" +checksum = "3c8146c105ae33d744e2d645f684d063b01176a99daf5986556266777b428816" dependencies = [ "futures-lite", "log", @@ -2032,31 +1911,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -2699,22 +2553,22 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "tinystr" @@ -3785,14 +3639,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1" dependencies = [ "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", "async-recursion", - "async-task", "async-trait", - "blocking", "enumflags2", "event-listener", "futures-core", diff --git a/Cargo.toml b/Cargo.toml index 81df799..a18c3cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,10 @@ sha2 = "0.11.0" hex = "0.4.3" xee-xpath = "0.1.5" xee-interpreter = "0.2.0" -notify-rust = "4.13.0" +notify-rust = { version = "4.13.0", default-features = false, features = ["z-with-tokio"] } tokio = "1.51.0" ashpd = { version = "0.13.9", features = ["open_uri"] } +futures-util = "0.3.32" [dev-dependencies] maplit = "1.0.2" diff --git a/src/main.rs b/src/main.rs index f803919..18c0328 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ use main_error::MainResult; use notify_debouncer_full::notify::event::{AccessKind, AccessMode, ModifyKind, RenameMode}; use notify_debouncer_full::notify::{EventKind, RecursiveMode}; use notify_debouncer_full::{DebounceEventResult, new_debouncer}; -use notify_rust::{Hint, Notification}; +use notify_rust::{ActionResponse, Hint, Notification}; use std::fs::{File, copy, create_dir_all, read_dir, remove_file, rename}; use std::io::ErrorKind; use std::os::unix::fs::symlink; @@ -17,6 +17,7 @@ use std::path::{Path, PathBuf}; use std::sync::mpsc::channel; use std::thread::{sleep, spawn}; use std::time::Duration; +use tokio::time::timeout; use tracing::{debug, error, info, instrument}; mod config; @@ -129,7 +130,11 @@ fn handle_watch_event( if let Some(new_path) = handle_file(&file, rules, remove_duplicates) { maybe_link_target(&new_path, link_target); if notify && path != new_path { - show_notification(new_path); + spawn_tokio(async { + if let Err(error) = show_notification(new_path).await { + error!(%error, "error with move notification"); + } + }) } }; } @@ -178,7 +183,7 @@ fn is_part(path: &Path) -> bool { path.extension().and_then(|ext| ext.to_str()) == Some("part") } -fn show_notification(source: PathBuf) { +async fn show_notification(source: PathBuf) -> Result<(), ashpd::Error> { debug!(file = %source.display(), "showing notification for file"); let parent = source.parent().unwrap(); match Notification::new() @@ -191,58 +196,69 @@ fn show_notification(source: PathBuf) { parent.display(), parent.file_name().unwrap().to_string_lossy() )) + .timeout(Duration::from_secs(10)) .hint(Hint::ActionIcons(true)) .hint(Hint::Category("transfer.complete".into())) .action("document-open", "Open") .action("folder-open", "Open Containing folder") - .show() + .show_async() + .await { Ok(notification) => { - spawn(move || { - notification.wait_for_action(|action| match action { - "document-open" => { - if let Err(error) = open_file(&source) { - error!(%error, file = %source.display(), "failed to open file from notification"); - } + let mut response = None; + let _ = timeout( + Duration::from_secs(11), + notification.wait_for_action_async(|action| { + if let ActionResponse::Custom(action) = action { + response = Some(String::from(*action)); } - "folder-open" => { - if let Err(error) = open_file(source.parent().unwrap()) { - error!(%error, file = %source.display(), "failed to open parent folder from notification"); - } + }), + ) + .await; + + match response.as_deref() { + Some("document-open") => { + if let Err(error) = open_file(&source).await { + error!(%error, file = %source.display(), "failed to open file from notification"); } - _ => (), - }); - }); + } + Some("folder-open") => { + if let Err(error) = open_file(source.parent().unwrap()).await { + error!(%error, file = %source.display(), "failed to open parent folder from notification"); + } + } + _ => {} + } } Err(error) => { error!(%error, "Failed to show notification"); } } + + Ok(()) } -fn open_file(path: impl AsRef) -> Result<(), ashpd::Error> { - let path = path.as_ref().to_owned(); +async fn open_file(path: impl AsRef) -> Result<(), ashpd::Error> { + let path = path.as_ref(); debug!(path = %path.display(), "sending open request"); - spawn_tokio_blocking(async move { - match File::open(&path) { - Ok(fd) => { - let result = if path.is_dir() { - let request = open_uri::OpenDirectoryRequest::default(); - request.send(&fd).await - } else { - let request = open_uri::OpenFileRequest::default().ask(false); - request.send_file(&fd).await - }; + match File::open(path) { + Ok(fd) => { + let result = if path.is_dir() { + let request = open_uri::OpenDirectoryRequest::default(); + request.send(&fd).await + } else { + let request = open_uri::OpenFileRequest::default().ask(false); + request.send_file(&fd).await + }; - result.inspect_err(|err| error!("Failed to send open request: {err}"))?; - Ok(()) - } - Err(err) => { - error!("Failed to open file: {err}"); - Err(ashpd::Error::IO(err)) - } + result.inspect_err(|err| error!("Failed to send open request: {err}"))?; + Ok(()) } - }) + Err(err) => { + error!("Failed to open file: {err}"); + Err(ashpd::Error::IO(err)) + } + } } fn maybe_link_target(source: &Path, target: Option<&str>) { @@ -281,7 +297,7 @@ fn match_file(file: &FileInfo, rules: &[Rule]) -> Option { None } -pub fn spawn_tokio_blocking(fut: F) -> F::Output +pub fn spawn_tokio(fut: F) where F: Future + Send + 'static, F::Output: Send + 'static, @@ -292,10 +308,8 @@ where .build() .expect("failed to build tokio runtime"); - rt.block_on(fut) - }) - .join() - .expect("fail for thread") + rt.block_on(fut); + }); } #[instrument(skip_all, fields(file = file.path))]