fix: throw catchable errors instead of crashing when publishing a message containing invalid UTF-8 over DDS (#2893)

This commit is contained in:
三咲雅 misaki masa 2025-06-18 19:30:31 +08:00 committed by GitHub
parent a0ce7c0ae7
commit 750ca0c0d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 145 additions and 97 deletions

View file

@ -7,6 +7,7 @@ use tokio::{fs::{self, OpenOptions}, io::AsyncWriteExt};
use yazi_config::YAZI; use yazi_config::YAZI;
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp, max_common_root, maybe_exists, paths_to_same_file}; use yazi_fs::{File, FilesOp, max_common_root, maybe_exists, paths_to_same_file};
use yazi_macro::err;
use yazi_proxy::{AppProxy, HIDER, TasksProxy, WATCHER}; use yazi_proxy::{AppProxy, HIDER, TasksProxy, WATCHER};
use yazi_shared::{terminal_clear, url::Url}; use yazi_shared::{terminal_clear, url::Url};
use yazi_term::tty::TTY; use yazi_term::tty::TTY;
@ -102,7 +103,8 @@ impl Mgr {
} }
if !succeeded.is_empty() { if !succeeded.is_empty() {
Pubsub::pub_from_bulk(succeeded.iter().map(|(o, n)| (o, &n.url)).collect()); let map = succeeded.iter().map(|(o, n)| (o, &n.url)).collect();
err!(Pubsub::pub_from_bulk(map));
FilesOp::rename(succeeded); FilesOp::rename(succeeded);
} }
drop(permit); drop(permit);

View file

@ -5,6 +5,7 @@ use tokio::fs;
use yazi_config::popup::{ConfirmCfg, InputCfg}; use yazi_config::popup::{ConfirmCfg, InputCfg};
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, paths_to_same_file, realname}; use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, paths_to_same_file, realname};
use yazi_macro::err;
use yazi_proxy::{ConfirmProxy, InputProxy, TabProxy, WATCHER}; use yazi_proxy::{ConfirmProxy, InputProxy, TabProxy, WATCHER};
use yazi_shared::{Id, event::CmdCow, url::{Url, UrnBuf}}; use yazi_shared::{Id, event::CmdCow, url::{Url, UrnBuf}};
@ -92,7 +93,7 @@ impl Mgr {
} }
TabProxy::reveal(&new); TabProxy::reveal(&new);
Pubsub::pub_from_rename(tab, &old, &new); err!(Pubsub::pub_from_rename(tab, &old, &new));
Ok(()) Ok(())
} }

View file

@ -3,6 +3,7 @@ use std::ops::{Deref, DerefMut};
use yazi_boot::BOOT; use yazi_boot::BOOT;
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_fs::File; use yazi_fs::File;
use yazi_macro::err;
use yazi_proxy::MgrProxy; use yazi_proxy::MgrProxy;
use yazi_shared::{Id, url::Url}; use yazi_shared::{Id, url::Url};
@ -38,7 +39,7 @@ impl Tabs {
self.cursor = idx; self.cursor = idx;
MgrProxy::refresh(); MgrProxy::refresh();
MgrProxy::peek(true); MgrProxy::peek(true);
Pubsub::pub_from_tab(self.active().id); err!(Pubsub::pub_from_tab(self.active().id));
} }
} }

View file

@ -43,10 +43,9 @@ impl Watcher {
} }
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
yazi_fs::mounts::Partitions::monitor( yazi_fs::mounts::Partitions::monitor(yazi_fs::mounts::PARTITIONS.clone(), || {
yazi_fs::mounts::PARTITIONS.clone(), yazi_macro::err!(yazi_dds::Pubsub::pub_from_mount())
yazi_dds::Pubsub::pub_from_mount, });
);
tokio::spawn(Self::fan_out(out_rx)); tokio::spawn(Self::fan_out(out_rx));
Self { in_tx, out_tx } Self { in_tx, out_tx }

View file

@ -2,6 +2,7 @@ use std::{collections::HashSet, ops::Deref};
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_fs::FilesOp; use yazi_fs::FilesOp;
use yazi_macro::err;
use yazi_shared::url::Url; use yazi_shared::url::Url;
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -65,7 +66,7 @@ impl Yanked {
} }
self.version = self.revision; self.version = self.revision;
Pubsub::pub_from_yank(self.cut, &self.urls); err!(Pubsub::pub_from_yank(self.cut, &self.urls));
true true
} }
} }

View file

@ -5,7 +5,7 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg; use yazi_config::popup::InputCfg;
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp, expand_path}; use yazi_fs::{File, FilesOp, expand_path};
use yazi_macro::render; use yazi_macro::{err, render};
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy, TabProxy}; use yazi_proxy::{CmpProxy, InputProxy, MgrProxy, TabProxy};
use yazi_shared::{Debounce, errors::InputError, event::CmdCow, url::Url}; use yazi_shared::{Debounce, errors::InputError, event::CmdCow, url::Url};
@ -73,7 +73,7 @@ impl Tab {
self.parent = Some(self.history.remove_or(&parent)); self.parent = Some(self.history.remove_or(&parent));
} }
Pubsub::pub_from_cd(self.id, self.cwd()); err!(Pubsub::pub_from_cd(self.id, self.cwd()));
self.hover(None); self.hover(None);
MgrProxy::refresh(); MgrProxy::refresh();

View file

@ -1,5 +1,5 @@
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_macro::render; use yazi_macro::{err, render};
use yazi_shared::{event::CmdCow, url::{Url, Urn}}; use yazi_shared::{event::CmdCow, url::{Url, Urn}};
use crate::tab::Tab; use crate::tab::Tab;
@ -25,7 +25,7 @@ impl Tab {
} }
// Publish through DDS // Publish through DDS
Pubsub::pub_from_hover(self.id, self.hovered().map(|h| &h.url)); err!(Pubsub::pub_from_hover(self.id, self.hovered().map(|h| &h.url)));
} }
fn hover_do(&mut self, url: Url) { fn hover_do(&mut self, url: Url) {

View file

@ -3,6 +3,7 @@ use std::mem;
use yazi_config::{LAYOUT, YAZI}; use yazi_config::{LAYOUT, YAZI};
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_fs::{File, Files, FilesOp, FolderStage, Step, cha::Cha}; use yazi_fs::{File, Files, FilesOp, FolderStage, Step, cha::Cha};
use yazi_macro::err;
use yazi_proxy::MgrProxy; use yazi_proxy::MgrProxy;
use yazi_shared::{Id, url::{Url, Urn, UrnBuf}}; use yazi_shared::{Id, url::{Url, Urn, UrnBuf}};
@ -88,7 +89,7 @@ impl Folder {
if !self.update(op) { if !self.update(op) {
return false; return false;
} else if self.stage != old { } else if self.stage != old {
Pubsub::pub_from_load(tab, &self.url, self.stage); err!(Pubsub::pub_from_load(tab, &self.url, self.stage));
} }
true true
} }

View file

@ -5,6 +5,7 @@ use parking_lot::RwLock;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time}; use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time};
use tracing::error; use tracing::error;
use yazi_macro::try_format;
use yazi_shared::{Id, RoCell}; use yazi_shared::{Id, RoCell};
use crate::{ClientReader, ClientWriter, Payload, Pubsub, Server, Stream, body::{Body, BodyBye, BodyHi}}; use crate::{ClientReader, ClientWriter, Payload, Pubsub, Server, Stream, body::{Body, BodyBye, BodyHi}};
@ -55,7 +56,7 @@ impl Client {
continue; continue;
} else if line.starts_with("hey,") { } else if line.starts_with("hey,") {
Self::handle_hey(&line); Self::handle_hey(&line);
} else if let Err(e) = Payload::from_str(&line).map(|p| p.emit()) { } else if let Err(e) = Payload::from_str(&line).and_then(|p| p.emit()) {
error!("Could not parse payload:\n{line}\n\nError:\n{e}"); error!("Could not parse payload:\n{line}\n\nError:\n{e}");
} }
} }
@ -68,11 +69,11 @@ impl Client {
pub async fn shot(kind: &str, receiver: Id, body: &str) -> Result<()> { pub async fn shot(kind: &str, receiver: Id, body: &str) -> Result<()> {
Body::validate(kind)?; Body::validate(kind)?;
let payload = format!( let payload = try_format!(
"{}\n{kind},{receiver},{ID},{body}\n{}\n", "{}\n{kind},{receiver},{ID},{body}\n{}\n",
Payload::new(BodyHi::borrowed(Default::default())), Payload::new(BodyHi::borrowed(Default::default())),
Payload::new(BodyBye::owned()) Payload::new(BodyBye::owned())
); )?;
let (mut lines, mut writer) = Stream::connect().await?; let (mut lines, mut writer) = Stream::connect().await?;
writer.write_all(payload.as_bytes()).await?; writer.write_all(payload.as_bytes()).await?;
@ -129,7 +130,7 @@ impl Client {
async fn make(kinds: &HashSet<&str>) -> Result<ClientReader> { async fn make(kinds: &HashSet<&str>) -> Result<ClientReader> {
let (lines, mut writer) = Stream::connect().await?; let (lines, mut writer) = Stream::connect().await?;
let hi = Payload::new(BodyHi::borrowed(kinds.clone())); let hi = Payload::new(BodyHi::borrowed(kinds.clone()));
writer.write_all(format!("{hi}\n").as_bytes()).await?; writer.write_all(try_format!("{hi}\n")?.as_bytes()).await?;
writer.flush().await?; writer.flush().await?;
Ok(lines) Ok(lines)
} }
@ -155,8 +156,8 @@ impl Client {
} }
#[inline] #[inline]
pub(super) fn push<'a>(payload: impl Into<Payload<'a>>) { pub(super) fn push<'a>(payload: impl Into<Payload<'a>>) -> Result<()> {
QUEUE_TX.send(format!("{}\n", payload.into())).ok(); Ok(QUEUE_TX.send(try_format!("{}\n", payload.into())?)?)
} }
#[inline] #[inline]

View file

@ -17,9 +17,12 @@ pub struct Payload<'a> {
impl<'a> Payload<'a> { impl<'a> Payload<'a> {
pub(super) fn new(body: Body<'a>) -> Self { Self { receiver: Id(0), sender: *ID, body } } pub(super) fn new(body: Body<'a>) -> Self { Self { receiver: Id(0), sender: *ID, body } }
pub(super) fn flush(&self) { writeln!(std::io::stdout(), "{self}").ok(); } pub(super) fn flush(&self) -> Result<()> {
writeln!(std::io::stdout(), "{self}")?;
Ok(())
}
pub(super) fn try_flush(&self) { pub(super) fn try_flush(&self) -> Result<()> {
let b = if self.receiver == 0 { let b = if self.receiver == 0 {
BOOT.remote_events.contains(self.body.kind()) BOOT.remote_events.contains(self.body.kind())
} else if let Body::Custom(b) = &self.body { } else if let Body::Custom(b) = &self.body {
@ -27,10 +30,7 @@ impl<'a> Payload<'a> {
} else { } else {
false false
}; };
if b { self.flush() } else { Ok(()) }
if b {
self.flush();
}
} }
pub(super) fn with_receiver(mut self, receiver: Id) -> Self { pub(super) fn with_receiver(mut self, receiver: Id) -> Self {
@ -45,9 +45,10 @@ impl<'a> Payload<'a> {
} }
impl Payload<'static> { impl Payload<'static> {
pub(super) fn emit(self) { pub(super) fn emit(self) -> Result<()> {
self.try_flush(); self.try_flush()?;
emit!(Call(Cmd::new("app:accept_payload").with_any("payload", self))); emit!(Call(Cmd::new("app:accept_payload").with_any("payload", self)));
Ok(())
} }
} }

View file

@ -1,5 +1,6 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use anyhow::Result;
use mlua::Function; use mlua::Function;
use parking_lot::RwLock; use parking_lot::RwLock;
use yazi_boot::BOOT; use yazi_boot::BOOT;
@ -62,159 +63,172 @@ impl Pubsub {
unsub!(REMOTE)(plugin, kind) && Self::pub_from_hi() unsub!(REMOTE)(plugin, kind) && Self::pub_from_hi()
} }
pub fn r#pub(body: Body<'static>) { body.with_receiver(*ID).emit(); } pub fn r#pub(body: Body<'static>) -> Result<()> { body.with_receiver(*ID).emit() }
pub fn pub_to(receiver: Id, body: Body<'static>) { pub fn pub_to(receiver: Id, body: Body<'static>) -> Result<()> {
if receiver == *ID { if receiver == *ID {
return Self::r#pub(body); return Self::r#pub(body);
} }
let kind = body.kind(); let kind = body.kind();
if receiver == 0 && Self::any_remote_own(kind) { if receiver == 0 && Self::any_remote_own(kind) {
Client::push(body); Client::push(body)?;
} else if PEERS.read().get(&receiver).is_some_and(|c| c.able(kind)) { } else if PEERS.read().get(&receiver).is_some_and(|c| c.able(kind)) {
Client::push(body.with_receiver(receiver)); Client::push(body.with_receiver(receiver))?;
} }
Ok(())
} }
pub fn pub_from_hi() -> bool { pub fn pub_from_hi() -> bool {
let abilities = REMOTE.read().keys().cloned().collect(); let abilities = REMOTE.read().keys().cloned().collect();
let abilities = BOOT.remote_events.union(&abilities).map(|s| s.as_str()).collect(); let abilities = BOOT.remote_events.union(&abilities).map(|s| s.as_str()).collect();
Client::push(BodyHi::borrowed(abilities)); // FIXME: handle error
Client::push(BodyHi::borrowed(abilities)).ok();
true true
} }
pub fn pub_from_tab(idx: Id) { pub fn pub_from_tab(idx: Id) -> Result<()> {
if LOCAL.read().contains_key("tab") { if LOCAL.read().contains_key("tab") {
Self::r#pub(BodyTab::owned(idx)); Self::r#pub(BodyTab::owned(idx))?;
} }
if PEERS.read().values().any(|p| p.able("tab")) { if PEERS.read().values().any(|p| p.able("tab")) {
Client::push(BodyTab::owned(idx)); Client::push(BodyTab::owned(idx))?;
} }
if BOOT.local_events.contains("tab") { if BOOT.local_events.contains("tab") {
BodyTab::owned(idx).with_receiver(*ID).flush(); BodyTab::owned(idx).with_receiver(*ID).flush()?;
} }
Ok(())
} }
pub fn pub_from_cd(tab: Id, url: &Url) { pub fn pub_from_cd(tab: Id, url: &Url) -> Result<()> {
if LOCAL.read().contains_key("cd") { if LOCAL.read().contains_key("cd") {
Self::r#pub(BodyCd::dummy(tab)); Self::r#pub(BodyCd::dummy(tab))?;
} }
if PEERS.read().values().any(|p| p.able("cd")) { if PEERS.read().values().any(|p| p.able("cd")) {
Client::push(BodyCd::borrowed(tab, url)); Client::push(BodyCd::borrowed(tab, url))?;
} }
if BOOT.local_events.contains("cd") { if BOOT.local_events.contains("cd") {
BodyCd::borrowed(tab, url).with_receiver(*ID).flush(); BodyCd::borrowed(tab, url).with_receiver(*ID).flush()?;
} }
Ok(())
} }
pub fn pub_from_load(tab: Id, url: &Url, stage: FolderStage) { pub fn pub_from_load(tab: Id, url: &Url, stage: FolderStage) -> Result<()> {
if LOCAL.read().contains_key("load") { if LOCAL.read().contains_key("load") {
Self::r#pub(BodyLoad::dummy(tab, url, stage)); Self::r#pub(BodyLoad::dummy(tab, url, stage))?;
} }
if PEERS.read().values().any(|p| p.able("load")) { if PEERS.read().values().any(|p| p.able("load")) {
Client::push(BodyLoad::borrowed(tab, url, stage)); Client::push(BodyLoad::borrowed(tab, url, stage))?;
} }
if BOOT.local_events.contains("load") { if BOOT.local_events.contains("load") {
BodyLoad::borrowed(tab, url, stage).with_receiver(*ID).flush(); BodyLoad::borrowed(tab, url, stage).with_receiver(*ID).flush()?;
} }
Ok(())
} }
pub fn pub_from_hover(tab: Id, url: Option<&Url>) { pub fn pub_from_hover(tab: Id, url: Option<&Url>) -> Result<()> {
if LOCAL.read().contains_key("hover") { if LOCAL.read().contains_key("hover") {
Self::r#pub(BodyHover::dummy(tab)); Self::r#pub(BodyHover::dummy(tab))?;
} }
if PEERS.read().values().any(|p| p.able("hover")) { if PEERS.read().values().any(|p| p.able("hover")) {
Client::push(BodyHover::borrowed(tab, url)); Client::push(BodyHover::borrowed(tab, url))?;
} }
if BOOT.local_events.contains("hover") { if BOOT.local_events.contains("hover") {
BodyHover::borrowed(tab, url).with_receiver(*ID).flush(); BodyHover::borrowed(tab, url).with_receiver(*ID).flush()?;
} }
Ok(())
} }
pub fn pub_from_rename(tab: Id, from: &Url, to: &Url) { pub fn pub_from_rename(tab: Id, from: &Url, to: &Url) -> Result<()> {
if LOCAL.read().contains_key("rename") { if LOCAL.read().contains_key("rename") {
Self::r#pub(BodyRename::dummy(tab, from, to)); Self::r#pub(BodyRename::dummy(tab, from, to))?;
} }
if PEERS.read().values().any(|p| p.able("rename")) { if PEERS.read().values().any(|p| p.able("rename")) {
Client::push(BodyRename::borrowed(tab, from, to)); Client::push(BodyRename::borrowed(tab, from, to))?;
} }
if BOOT.local_events.contains("rename") { if BOOT.local_events.contains("rename") {
BodyRename::borrowed(tab, from, to).with_receiver(*ID).flush(); BodyRename::borrowed(tab, from, to).with_receiver(*ID).flush()?;
} }
Ok(())
} }
pub fn pub_from_bulk(changes: HashMap<&Url, &Url>) { pub fn pub_from_bulk(changes: HashMap<&Url, &Url>) -> Result<()> {
if LOCAL.read().contains_key("bulk") { if LOCAL.read().contains_key("bulk") {
Self::r#pub(BodyBulk::owned(&changes)); Self::r#pub(BodyBulk::owned(&changes))?;
} }
if PEERS.read().values().any(|p| p.able("bulk")) { if PEERS.read().values().any(|p| p.able("bulk")) {
Client::push(BodyBulk::borrowed(&changes)); Client::push(BodyBulk::borrowed(&changes))?;
} }
if BOOT.local_events.contains("bulk") { if BOOT.local_events.contains("bulk") {
BodyBulk::borrowed(&changes).with_receiver(*ID).flush(); BodyBulk::borrowed(&changes).with_receiver(*ID).flush()?;
} }
Ok(())
} }
pub fn pub_from_yank(cut: bool, urls: &HashSet<Url>) { pub fn pub_from_yank(cut: bool, urls: &HashSet<Url>) -> Result<()> {
if LOCAL.read().contains_key("@yank") { if LOCAL.read().contains_key("@yank") {
Self::r#pub(BodyYank::dummy()); Self::r#pub(BodyYank::dummy())?;
} }
if Self::any_remote_own("@yank") { if Self::any_remote_own("@yank") {
Client::push(BodyYank::borrowed(cut, urls)); Client::push(BodyYank::borrowed(cut, urls))?;
} }
if BOOT.local_events.contains("@yank") { if BOOT.local_events.contains("@yank") {
BodyYank::borrowed(cut, urls).with_receiver(*ID).flush(); BodyYank::borrowed(cut, urls).with_receiver(*ID).flush()?;
} }
Ok(())
} }
pub(super) fn pub_from_move(items: Vec<BodyMoveItem>) { pub(super) fn pub_from_move(items: Vec<BodyMoveItem>) -> Result<()> {
if PEERS.read().values().any(|p| p.able("move")) { if PEERS.read().values().any(|p| p.able("move")) {
Client::push(BodyMove::borrowed(&items)); Client::push(BodyMove::borrowed(&items))?;
} }
if BOOT.local_events.contains("move") { if BOOT.local_events.contains("move") {
BodyMove::borrowed(&items).with_receiver(*ID).flush(); BodyMove::borrowed(&items).with_receiver(*ID).flush()?;
} }
if LOCAL.read().contains_key("move") { if LOCAL.read().contains_key("move") {
Self::r#pub(BodyMove::owned(items)); Self::r#pub(BodyMove::owned(items))?;
} }
Ok(())
} }
pub(super) fn pub_from_trash(urls: Vec<Url>) { pub(super) fn pub_from_trash(urls: Vec<Url>) -> Result<()> {
if PEERS.read().values().any(|p| p.able("trash")) { if PEERS.read().values().any(|p| p.able("trash")) {
Client::push(BodyTrash::borrowed(&urls)); Client::push(BodyTrash::borrowed(&urls))?;
} }
if BOOT.local_events.contains("trash") { if BOOT.local_events.contains("trash") {
BodyTrash::borrowed(&urls).with_receiver(*ID).flush(); BodyTrash::borrowed(&urls).with_receiver(*ID).flush()?;
} }
if LOCAL.read().contains_key("trash") { if LOCAL.read().contains_key("trash") {
Self::r#pub(BodyTrash::owned(urls)); Self::r#pub(BodyTrash::owned(urls))?;
} }
Ok(())
} }
pub(super) fn pub_from_delete(urls: Vec<Url>) { pub(super) fn pub_from_delete(urls: Vec<Url>) -> Result<()> {
if PEERS.read().values().any(|p| p.able("delete")) { if PEERS.read().values().any(|p| p.able("delete")) {
Client::push(BodyDelete::borrowed(&urls)); Client::push(BodyDelete::borrowed(&urls))?;
} }
if BOOT.local_events.contains("delete") { if BOOT.local_events.contains("delete") {
BodyDelete::borrowed(&urls).with_receiver(*ID).flush(); BodyDelete::borrowed(&urls).with_receiver(*ID).flush()?;
} }
if LOCAL.read().contains_key("delete") { if LOCAL.read().contains_key("delete") {
Self::r#pub(BodyDelete::owned(urls)); Self::r#pub(BodyDelete::owned(urls))?;
} }
Ok(())
} }
pub fn pub_from_mount() { pub fn pub_from_mount() -> Result<()> {
if LOCAL.read().contains_key("mount") { if LOCAL.read().contains_key("mount") {
Self::r#pub(BodyMount::owned()); Self::r#pub(BodyMount::owned())?;
} }
if PEERS.read().values().any(|p| p.able("mount")) { if PEERS.read().values().any(|p| p.able("mount")) {
Client::push(BodyMount::owned()); Client::push(BodyMount::owned())?;
} }
if BOOT.local_events.contains("mount") { if BOOT.local_events.contains("mount") {
BodyMount::owned().with_receiver(*ID).flush(); BodyMount::owned().with_receiver(*ID).flush()?;
} }
Ok(())
} }
#[inline] #[inline]

View file

@ -4,6 +4,7 @@ use parking_lot::Mutex;
use tokio::{pin, select, sync::mpsc}; use tokio::{pin, select, sync::mpsc};
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use yazi_macro::err;
use yazi_shared::{RoCell, url::Url}; use yazi_shared::{RoCell, url::Url};
use crate::{Pubsub, body::BodyMoveItem}; use crate::{Pubsub, body::BodyMoveItem};
@ -61,9 +62,9 @@ impl Pump {
loop { loop {
select! { select! {
Some(items) = move_rx.next() => Pubsub::pub_from_move(items), Some(items) = move_rx.next() => err!(Pubsub::pub_from_move(items)),
Some(urls) = trash_rx.next() => Pubsub::pub_from_trash(urls), Some(urls) = trash_rx.next() => err!(Pubsub::pub_from_trash(urls)),
Some(urls) = delete_rx.next() => Pubsub::pub_from_delete(urls), Some(urls) = delete_rx.next() => err!(Pubsub::pub_from_delete(urls)),
else => { else => {
CT.cancel(); CT.cancel();
break; break;

View file

@ -3,6 +3,7 @@ use std::{collections::HashMap, str::FromStr, time::Duration};
use anyhow::Result; use anyhow::Result;
use parking_lot::RwLock; use parking_lot::RwLock;
use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle, time}; use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle, time};
use yazi_macro::try_format;
use yazi_shared::{Id, RoCell}; use yazi_shared::{Id, RoCell};
use crate::{Client, ClientWriter, Payload, Peer, STATE, Stream, body::{Body, BodyBye, BodyHey}}; use crate::{Client, ClientWriter, Payload, Peer, STATE, Stream, body::{Body, BodyBye, BodyHey}};
@ -109,13 +110,12 @@ impl Server {
} }
fn handle_hey(clients: &HashMap<Id, Client>) { fn handle_hey(clients: &HashMap<Id, Client>) {
let payload = format!( let payload = Payload::new(BodyHey::owned(
"{}\n", clients.values().map(|c| (c.id, Peer::new(&c.abilities))).collect(),
Payload::new(BodyHey::owned( ));
clients.values().map(|c| (c.id, Peer::new(&c.abilities))).collect() if let Ok(s) = try_format!("{payload}\n") {
)) clients.values().for_each(|c| _ = c.tx.send(s.clone()));
); }
clients.values().for_each(|c| _ = c.tx.send(payload.clone()));
} }
async fn handle_bye(id: Id, mut rx: UnboundedReceiver<String>, mut writer: ClientWriter) { async fn handle_bye(id: Id, mut rx: UnboundedReceiver<String>, mut writer: ClientWriter) {
@ -125,10 +125,10 @@ impl Server {
} }
} }
_ = writer let bye = BodyBye::owned().with_receiver(id).with_sender(Id(0));
.write_all(BodyBye::owned().with_receiver(id).with_sender(Id(0)).to_string().as_bytes()) if let Ok(s) = try_format!("{bye}") {
.await; writer.write_all(s.as_bytes()).await.ok();
writer.flush().await.ok();
writer.flush().await.ok(); }
} }
} }

View file

@ -12,7 +12,10 @@ use yazi_shared::natsort;
use super::{Locked, Partition, Partitions}; use super::{Locked, Partition, Partitions};
impl Partitions { impl Partitions {
pub fn monitor(me: Locked, cb: fn()) { pub fn monitor<F>(me: Locked, cb: F)
where
F: Fn() + Copy + Send + 'static,
{
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let session = unsafe { DASessionCreate(kCFAllocatorDefault) }; let session = unsafe { DASessionCreate(kCFAllocatorDefault) };
if session.is_null() { if session.is_null() {
@ -41,7 +44,7 @@ impl Partitions {
if mem::replace(&mut me.write().need_update, true) { if mem::replace(&mut me.write().need_update, true) {
return; return;
} }
Self::update(me.clone(), cb); Self::update(me.clone(), move || _ = cb());
}); });
Box::into_raw(Box::new(boxed)) as *mut c_void Box::into_raw(Box::new(boxed)) as *mut c_void
}; };

9
yazi-macro/src/fmt.rs Normal file
View file

@ -0,0 +1,9 @@
#[macro_export]
macro_rules! try_format {
($($arg:tt)*) => {{
use std::fmt::Write;
let mut output = String::new();
output.write_fmt(format_args!($($arg)*)).map(|_| output)
}}
}

View file

@ -1,5 +1,6 @@
mod asset; mod asset;
mod event; mod event;
mod fmt;
mod log; mod log;
mod module; mod module;
mod platform; mod platform;

View file

@ -17,3 +17,18 @@ macro_rules! time {
} }
}}; }};
} }
#[macro_export]
macro_rules! err {
($expr:expr) => {
$crate::err!(stringify!($expr), $expr)
};
($label:expr, $expr:expr) => {
$crate::err!($expr, "{}", $label)
};
($expr:expr, $fmt:expr, $($args:tt)*) => {{
if let Err(e) = $expr {
tracing::error!("{} failed: {e}", format_args!($fmt, $($args)*));
}
}};
}

View file

@ -9,15 +9,13 @@ pub struct Pubsub;
impl Pubsub { impl Pubsub {
pub(super) fn r#pub(lua: &Lua) -> mlua::Result<Function> { pub(super) fn r#pub(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (kind, value): (mlua::String, Value)| { lua.create_function(|_, (kind, value): (mlua::String, Value)| {
yazi_dds::Pubsub::r#pub(Body::from_lua(&kind.to_str()?, value)?); yazi_dds::Pubsub::r#pub(Body::from_lua(&kind.to_str()?, value)?).into_lua_err()
Ok(())
}) })
} }
pub(super) fn pub_to(lua: &Lua) -> mlua::Result<Function> { pub(super) fn pub_to(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (receiver, kind, value): (Id, mlua::String, Value)| { lua.create_function(|_, (receiver, kind, value): (Id, mlua::String, Value)| {
yazi_dds::Pubsub::pub_to(*receiver, Body::from_lua(&kind.to_str()?, value)?); yazi_dds::Pubsub::pub_to(*receiver, Body::from_lua(&kind.to_str()?, value)?).into_lua_err()
Ok(())
}) })
} }