diff --git a/Cargo.lock b/Cargo.lock index 1608b935..94e6cbbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2734,6 +2734,7 @@ dependencies = [ "clap_complete", "clap_complete_fig", "clap_complete_nushell", + "serde_json", "tokio", "yazi-dds", ] diff --git a/yazi-boot/src/args.rs b/yazi-boot/src/args.rs index 92674192..07e8c12f 100644 --- a/yazi-boot/src/args.rs +++ b/yazi-boot/src/args.rs @@ -17,18 +17,18 @@ pub struct Args { pub chooser_file: Option, /// Clear the cache directory - #[arg(long, action)] + #[arg(long)] pub clear_cache: bool, /// Report the specified local events to stdout - #[arg(long, action)] + #[arg(long)] pub local_events: Option, /// Report the specified remote events to stdout - #[arg(long, action)] + #[arg(long)] pub remote_events: Option, /// Print debug information - #[arg(long, action)] + #[arg(long)] pub debug: bool, /// Print version diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index a90aac13..9e02786a 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -12,15 +12,18 @@ repository = "https://github.com/sxyazi/yazi" yazi-dds = { path = "../yazi-dds", version = "0.2.4" } # External dependencies -anyhow = "1.0.82" -clap = { version = "4.5.4", features = [ "derive" ] } -tokio = { version = "1.37.0", features = [ "full" ] } +anyhow = "1.0.82" +clap = { version = "4.5.4", features = [ "derive" ] } +serde_json = "1.0.116" +tokio = { version = "1.37.0", features = [ "full" ] } [build-dependencies] +anyhow = "1.0.82" clap = { version = "4.5.4", features = [ "derive" ] } clap_complete = "4.5.2" -clap_complete_nushell = "4.5.1" clap_complete_fig = "4.5.0" +clap_complete_nushell = "4.5.1" +serde_json = "1.0.116" [[bin]] name = "ya" diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index 3ef75349..554f2a1b 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -1,7 +1,10 @@ +use std::borrow::Cow; + +use anyhow::{bail, Result}; use clap::{command, Parser, Subcommand}; #[derive(Parser)] -#[command(name = "ya",version, about, long_about = None)] +#[command(name = "ya", version, about, long_about = None)] #[command(propagate_version = true)] pub(super) struct Args { #[command(subcommand)] @@ -10,11 +13,66 @@ pub(super) struct Args { #[derive(Subcommand)] pub(super) enum Command { - /// Send a message to remote instances. - Send(CommandSend), + /// Publish a message to remote instance(s). + Pub(CommandPub), + /// Publish a static message to all remote instances. + PubStatic(CommandPubStatic), } #[derive(clap::Args)] -pub(super) struct CommandSend { - pub(super) message: String, +pub(super) struct CommandPub { + /// The receiver ID. + #[arg(index = 1)] + pub(super) receiver: u64, + /// The kind of message. + #[arg(index = 2)] + pub(super) kind: String, + /// Send the message with a string body. + #[arg(long)] + pub(super) str: Option, + /// Send the message with a JSON body. + #[arg(long)] + pub(super) json: Option, +} + +impl CommandPub { + #[allow(dead_code)] + pub(super) fn body(&self) -> Result> { + if let Some(json) = &self.json { + Ok(json.into()) + } else if let Some(str) = &self.str { + Ok(serde_json::to_string(str)?.into()) + } else { + bail!("No body provided"); + } + } +} + +#[derive(clap::Args)] +pub(super) struct CommandPubStatic { + /// The severity of the message. + #[arg(index = 1)] + pub(super) severity: u16, + /// The kind of message. + #[arg(index = 2)] + pub(super) kind: String, + /// Send the message with a string body. + #[arg(long)] + pub(super) str: Option, + /// Send the message with a JSON body. + #[arg(long)] + pub(super) json: Option, +} + +impl CommandPubStatic { + #[allow(dead_code)] + pub(super) fn body(&self) -> Result> { + if let Some(json) = &self.json { + Ok(json.into()) + } else if let Some(str) = &self.str { + Ok(serde_json::to_string(str)?.into()) + } else { + bail!("No body provided"); + } + } } diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index 6a7e222f..b4ab6b03 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -8,9 +8,16 @@ async fn main() -> anyhow::Result<()> { let args = Args::parse(); match &args.command { - Command::Send(cmd) => { + Command::Pub(cmd) => { yazi_dds::init(); - if let Err(e) = yazi_dds::Client::shot(&cmd.message).await { + if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver, None, &cmd.body()?).await { + eprintln!("Cannot send message: {e}"); + std::process::exit(1); + } + } + Command::PubStatic(cmd) => { + yazi_dds::init(); + if let Err(e) = yazi_dds::Client::shot(&cmd.kind, 0, Some(cmd.severity), &cmd.body()?).await { eprintln!("Cannot send message: {e}"); std::process::exit(1); } diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index af7f542b..b1eb01c7 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -38,7 +38,7 @@ tab_width = 1 # Count count_copied = { fg = "white", bg = "green" } count_cut = { fg = "white", bg = "red" } -count_selected = { fg = "white", bg = "blue" } +count_selected = { fg = "white", bg = "yellow" } # Border border_symbol = "│" diff --git a/yazi-dds/src/body/body.rs b/yazi-dds/src/body/body.rs index 55e3793f..718ff4b3 100644 --- a/yazi-dds/src/body/body.rs +++ b/yazi-dds/src/body/body.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use mlua::{ExternalResult, IntoLua, Lua, Value}; use serde::Serialize; @@ -40,15 +40,9 @@ impl Body<'static> { }) } - pub fn from_lua(kind: &str, value: Value) -> Result { - Ok(match kind { - "hi" | "hey" | "bye" | "cd" | "hover" | "rename" | "bulk" | "yank" | "move" | "trash" - | "delete" => Err("Cannot construct system event").into_lua_err()?, - _ if !kind.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-') => { - Err("Kind must be alphanumeric with dashes").into_lua_err()? - } - _ => BodyCustom::from_lua(kind, value)?, - }) + pub fn from_lua(kind: &str, value: Value) -> mlua::Result { + Self::validate(kind).into_lua_err()?; + BodyCustom::from_lua(kind, value) } pub fn tab(kind: &str, body: &str) -> usize { @@ -65,6 +59,27 @@ impl Body<'static> { _ => 0, } } + + pub fn validate(kind: &str) -> Result<()> { + if matches!( + kind, + "hi" + | "hey" | "bye" + | "cd" | "hover" + | "rename" + | "bulk" | "yank" + | "move" | "trash" + | "delete" + ) { + bail!("Cannot construct system event"); + } + + if !kind.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-') { + bail!("Kind must be alphanumeric with dashes"); + } + + Ok(()) + } } impl<'a> Body<'a> { diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index d0d7e03d..de3d0493 100644 --- a/yazi-dds/src/client.rs +++ b/yazi-dds/src/client.rs @@ -1,6 +1,6 @@ use std::{collections::{HashMap, HashSet}, mem, str::FromStr}; -use anyhow::{bail, Result}; +use anyhow::Result; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time}; @@ -62,16 +62,13 @@ impl Client { }); } - pub async fn shot(s: &str) -> Result<()> { - let (kind, receiver, sender, body) = Payload::split(s)?; - if receiver != 0 && sender <= u16::MAX as u64 { - bail!("Sender must be greater than 65535 if receiver is non-zero"); - } + pub async fn shot(kind: &str, receiver: u64, severity: Option, body: &str) -> Result<()> { + Body::validate(kind)?; + let sender = severity.map(Into::into).unwrap_or(*ID); let payload = format!( - "{}\n{kind},{receiver},{sender},{}\n{}\n", + "{}\n{kind},{receiver},{sender},{body}\n{}\n", Payload::new(BodyHi::borrowed(Default::default())), - serde_json::to_string(body)?, Payload::new(BodyBye::borrowed()) ); @@ -81,7 +78,7 @@ impl Client { drop(writer); while let Ok(Some(s)) = lines.next_line().await { - if matches!(Payload::split(&s), Ok((kind, ..)) if kind == "bye") { + if matches!(s.split(',').next(), Some(kind) if kind == "bye") { break; } } diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index 855a7794..bdd9621a 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -49,7 +49,16 @@ impl<'a> Payload<'a> { } impl Payload<'static> { - pub fn split(s: &str) -> Result<(&str, u64, u64, &str)> { + pub(super) fn emit(self) { + self.try_flush(); + emit!(Call(Cmd::new("accept_payload").with_any("payload", self), Layer::App)); + } +} + +impl FromStr for Payload<'static> { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { let mut parts = s.splitn(4, ','); let kind = parts.next().ok_or_else(|| anyhow!("empty kind"))?; @@ -62,20 +71,6 @@ impl Payload<'static> { let body = parts.next().ok_or_else(|| anyhow!("empty body"))?; - Ok((kind, receiver, sender, body)) - } - - pub(super) fn emit(self) { - self.try_flush(); - emit!(Call(Cmd::new("accept_payload").with_any("payload", self), Layer::App)); - } -} - -impl FromStr for Payload<'static> { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let (kind, receiver, sender, body) = Self::split(s)?; Ok(Self { receiver, sender, body: Body::from_str(kind, body)? }) } } diff --git a/yazi-plugin/src/pubsub/pubsub.rs b/yazi-plugin/src/pubsub/pubsub.rs index 08aaf7bf..eb505648 100644 --- a/yazi-plugin/src/pubsub/pubsub.rs +++ b/yazi-plugin/src/pubsub/pubsub.rs @@ -12,7 +12,7 @@ impl Pubsub { ps.raw_set( "pub", lua.create_function(|_, (kind, value): (mlua::String, Value)| { - yazi_dds::Pubsub::pub_(Body::from_lua(kind.to_str()?, value).into_lua_err()?); + yazi_dds::Pubsub::pub_(Body::from_lua(kind.to_str()?, value)?); Ok(()) })?, )?; @@ -20,7 +20,7 @@ impl Pubsub { ps.raw_set( "pub_to", lua.create_function(|_, (receiver, kind, value): (u64, mlua::String, Value)| { - yazi_dds::Pubsub::pub_to(receiver, Body::from_lua(kind.to_str()?, value).into_lua_err()?); + yazi_dds::Pubsub::pub_to(receiver, Body::from_lua(kind.to_str()?, value)?); Ok(()) })?, )?; @@ -28,10 +28,7 @@ impl Pubsub { ps.raw_set( "pub_static", lua.create_function(|_, (severity, kind, value): (u16, mlua::String, Value)| { - yazi_dds::Pubsub::pub_static( - severity, - Body::from_lua(kind.to_str()?, value).into_lua_err()?, - ); + yazi_dds::Pubsub::pub_static(severity, Body::from_lua(kind.to_str()?, value)?); Ok(()) })?, )?;