From e4d67121f8ee99df24afab48e7dae6d69455c7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sun, 2 Jun 2024 03:04:53 +0800 Subject: [PATCH] feat!: DDS client-server version check (#1111) --- Cargo.lock | 17 +++++++------- yazi-adaptor/Cargo.toml | 2 +- yazi-boot/Cargo.toml | 2 +- yazi-cli/Cargo.toml | 2 +- yazi-cli/src/args.rs | 31 +++++++++++++++++-------- yazi-cli/src/main.rs | 2 +- yazi-config/Cargo.toml | 2 +- yazi-core/Cargo.toml | 4 ++-- yazi-core/src/tab/commands/escape.rs | 32 +++++++++++++------------- yazi-dds/Cargo.toml | 7 ++++-- yazi-dds/build.rs | 9 ++++++++ yazi-dds/src/body/bye.rs | 4 ++-- yazi-dds/src/body/hey.rs | 12 ++++++++-- yazi-dds/src/body/hi.rs | 10 +++++++- yazi-dds/src/client.rs | 24 ++++++++++++++++---- yazi-dds/src/server.rs | 34 ++++++++++++++++++---------- yazi-fm/Cargo.toml | 2 +- yazi-plugin/Cargo.toml | 4 ++-- yazi-proxy/Cargo.toml | 2 +- yazi-scheduler/Cargo.toml | 2 +- yazi-shared/Cargo.toml | 4 ++-- 21 files changed, 136 insertions(+), 72 deletions(-) create mode 100644 yazi-dds/build.rs diff --git a/Cargo.lock b/Cargo.lock index d49e8ff1d..52920979e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1698,9 +1698,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -1717,9 +1717,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -2052,9 +2052,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -2071,9 +2071,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", @@ -2807,6 +2807,7 @@ dependencies = [ "tokio-stream", "tokio-util", "uzers", + "vergen", "yazi-boot", "yazi-shared", ] diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index ca61b09f9..8a7a64419 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -25,7 +25,7 @@ imagesize = "0.12.0" kamadak-exif = "0.5.5" ratatui = "0.26.3" scopeguard = "1.2.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 38160f2b0..46e15acc1 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies clap = { version = "4.5.4", features = [ "derive" ] } -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } [build-dependencies] clap = { version = "4.5.4", features = [ "derive" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 7b14c0601..457e5b5f9 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.5.4", features = [ "derive" ] } crossterm = "0.27.0" md-5 = "0.10.6" serde_json = "1.0.117" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } toml_edit = "0.22.13" [build-dependencies] diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index c583da249..cb8f39bde 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -26,12 +26,12 @@ pub(super) enum Command { #[derive(clap::Args)] pub(super) struct CommandPub { - /// The receiver ID. - #[arg(index = 1)] - pub(super) receiver: u64, /// The kind of message. - #[arg(index = 2)] + #[arg(index = 1)] pub(super) kind: String, + /// The receiver ID. + #[arg(index = 2)] + pub(super) receiver: Option, /// Send the message with a string body. #[arg(long)] pub(super) str: Option, @@ -41,6 +41,17 @@ pub(super) struct CommandPub { } impl CommandPub { + #[allow(dead_code)] + pub(super) fn receiver(&self) -> Result { + if let Some(receiver) = self.receiver { + Ok(receiver) + } else if let Ok(s) = std::env::var("YAZI_ID") { + Ok(s.parse()?) + } else { + bail!("No receiver ID provided, also no YAZI_ID environment variable found.") + } + } + #[allow(dead_code)] pub(super) fn body(&self) -> Result> { if let Some(json) = &self.json { @@ -48,19 +59,19 @@ impl CommandPub { } else if let Some(str) = &self.str { Ok(serde_json::to_string(str)?.into()) } else { - bail!("No body provided"); + Ok("".into()) } } } #[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)] + #[arg(index = 1)] pub(super) kind: String, + /// The severity of the message. + #[arg(index = 2)] + pub(super) severity: u16, /// Send the message with a string body. #[arg(long)] pub(super) str: Option, @@ -77,7 +88,7 @@ impl CommandPubStatic { } else if let Some(str) = &self.str { Ok(serde_json::to_string(str)?.into()) } else { - bail!("No body provided"); + Ok("".into()) } } } diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index d4597af06..e7aacaf04 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -19,7 +19,7 @@ async fn main() -> anyhow::Result<()> { match Args::parse().command { Command::Pub(cmd) => { yazi_dds::init(); - if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver, None, &cmd.body()?).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); } diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index e6c6348fb..18f5fafab 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -18,7 +18,7 @@ crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" ratatui = "0.26.3" -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" toml = { version = "0.8.13", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 39b856961..4d6358ee7 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -29,9 +29,9 @@ parking_lot = "0.12.3" ratatui = "0.26.3" regex = "1.10.4" scopeguard = "1.2.0" -serde = "1.0.202" +serde = "1.0.203" shell-words = "1.1.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index 23bbc0d2e..b05b5fb34 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -8,8 +8,8 @@ bitflags! { pub struct Opt: u8 { const FIND = 0b00001; const VISUAL = 0b00010; - const SELECT = 0b00100; - const FILTER = 0b01000; + const FILTER = 0b00100; + const SELECT = 0b01000; const SEARCH = 0b10000; } } @@ -21,8 +21,8 @@ impl From for Opt { ("all", true) => Self::all(), ("find", true) => acc | Self::FIND, ("visual", true) => acc | Self::VISUAL, - ("select", true) => acc | Self::SELECT, ("filter", true) => acc | Self::FILTER, + ("select", true) => acc | Self::SELECT, ("search", true) => acc | Self::SEARCH, _ => acc, } @@ -36,8 +36,8 @@ impl Tab { if opt.is_empty() { _ = self.escape_find() || self.escape_visual() - || self.escape_select() || self.escape_filter() + || self.escape_select() || self.escape_search(); return; } @@ -48,12 +48,12 @@ impl Tab { if opt.contains(Opt::VISUAL) { self.escape_visual(); } - if opt.contains(Opt::SELECT) { - self.escape_select(); - } if opt.contains(Opt::FILTER) { self.escape_filter(); } + if opt.contains(Opt::SELECT) { + self.escape_select(); + } if opt.contains(Opt::SEARCH) { self.escape_search(); } @@ -70,24 +70,24 @@ impl Tab { true } - pub fn escape_select(&mut self) -> bool { - if self.selected.is_empty() { + pub fn escape_filter(&mut self) -> bool { + if self.current.files.filter().is_none() { return false; } - self.selected.clear(); - if self.current.hovered().is_some_and(|h| h.is_dir()) { - ManagerProxy::peek(true); - } + self.filter_do(super::filter::Opt::default()); render_and!(true) } - pub fn escape_filter(&mut self) -> bool { - if self.current.files.filter().is_none() { + pub fn escape_select(&mut self) -> bool { + if self.selected.is_empty() { return false; } - self.filter_do(super::filter::Opt::default()); + self.selected.clear(); + if self.current.hovered().is_some_and(|h| h.is_dir()) { + ManagerProxy::peek(true); + } render_and!(true) } diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index d4a5b7843..0b07070ba 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -20,11 +20,14 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } anyhow = "1.0.86" mlua = { version = "0.9.8", features = [ "lua54" ] } parking_lot = "0.12.3" -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } serde_json = "1.0.117" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" +[build-dependencies] +vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } + [target."cfg(unix)".dependencies] uzers = "0.12.0" diff --git a/yazi-dds/build.rs b/yazi-dds/build.rs new file mode 100644 index 000000000..aef969b94 --- /dev/null +++ b/yazi-dds/build.rs @@ -0,0 +1,9 @@ +use std::error::Error; + +use vergen::EmitBuilder; + +fn main() -> Result<(), Box> { + EmitBuilder::builder().git_sha(true).emit()?; + + Ok(()) +} diff --git a/yazi-dds/src/body/bye.rs b/yazi-dds/src/body/bye.rs index 898af34bd..256bd1d93 100644 --- a/yazi-dds/src/body/bye.rs +++ b/yazi-dds/src/body/bye.rs @@ -4,11 +4,11 @@ use serde::{Deserialize, Serialize}; use super::Body; #[derive(Debug, Serialize, Deserialize)] -pub struct BodyBye {} +pub struct BodyBye; impl BodyBye { #[inline] - pub fn borrowed() -> Body<'static> { Self {}.into() } + pub fn owned() -> Body<'static> { Self.into() } } impl<'a> From for Body<'a> { diff --git a/yazi-dds/src/body/hey.rs b/yazi-dds/src/body/hey.rs index 49e6bb6e6..58d8cf93a 100644 --- a/yazi-dds/src/body/hey.rs +++ b/yazi-dds/src/body/hey.rs @@ -3,12 +3,20 @@ use std::collections::HashMap; use mlua::{ExternalResult, IntoLua, Lua, Value}; use serde::{Deserialize, Serialize}; -use super::Body; +use super::{Body, BodyHi}; use crate::Peer; #[derive(Debug, Serialize, Deserialize)] pub struct BodyHey { - pub peers: HashMap, + pub peers: HashMap, + pub version: String, +} + +impl BodyHey { + #[inline] + pub fn owned(peers: HashMap) -> Body<'static> { + Self { peers, version: BodyHi::version() }.into() + } } impl From for Body<'_> { diff --git a/yazi-dds/src/body/hi.rs b/yazi-dds/src/body/hi.rs index b536b55de..c60a2370a 100644 --- a/yazi-dds/src/body/hi.rs +++ b/yazi-dds/src/body/hi.rs @@ -8,13 +8,21 @@ use super::Body; #[derive(Debug, Serialize, Deserialize)] pub struct BodyHi<'a> { pub abilities: HashSet>, + pub version: String, } impl<'a> BodyHi<'a> { #[inline] pub fn borrowed(abilities: HashSet<&'a String>) -> Body<'a> { - Self { abilities: abilities.into_iter().map(Cow::Borrowed).collect() }.into() + Self { + abilities: abilities.into_iter().map(Cow::Borrowed).collect(), + version: Self::version(), + } + .into() } + + #[inline] + pub fn version() -> String { format!("{} {}", env!("CARGO_PKG_VERSION"), env!("VERGEN_GIT_SHA")) } } impl<'a> From> for Body<'a> { diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index de3d0493f..b9bf8d18a 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::Result; +use anyhow::{bail, Result}; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time}; @@ -69,7 +69,7 @@ impl Client { let payload = format!( "{}\n{kind},{receiver},{sender},{body}\n{}\n", Payload::new(BodyHi::borrowed(Default::default())), - Payload::new(BodyBye::borrowed()) + Payload::new(BodyBye::owned()) ); let (mut lines, mut writer) = Stream::connect().await?; @@ -77,12 +77,26 @@ impl Client { writer.flush().await?; drop(writer); - while let Ok(Some(s)) = lines.next_line().await { - if matches!(s.split(',').next(), Some(kind) if kind == "bye") { - break; + let mut version = None; + while let Ok(Some(line)) = lines.next_line().await { + match line.split(',').next() { + Some("hey") if version.is_none() => { + if let Ok(Body::Hey(hey)) = Payload::from_str(&line).map(|p| p.body) { + version = Some(hey.version); + } + } + Some("bye") => break, + _ => {} } } + if version != Some(BodyHi::version()) { + bail!( + "Incompatible version (Ya {}, Yazi {})", + BodyHi::version(), + version.as_deref().unwrap_or("Unknown") + ); + } Ok(()) } diff --git a/yazi-dds/src/server.rs b/yazi-dds/src/server.rs index b3e51b124..45d82cda5 100644 --- a/yazi-dds/src/server.rs +++ b/yazi-dds/src/server.rs @@ -2,10 +2,10 @@ use std::{collections::HashMap, str::FromStr, time::Duration}; use anyhow::Result; use parking_lot::RwLock; -use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc, task::JoinHandle, time}; +use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle, time}; use yazi_shared::RoCell; -use crate::{body::{Body, BodyBye, BodyHey}, Client, Payload, Peer, Stream, STATE}; +use crate::{body::{Body, BodyBye, BodyHey}, Client, ClientWriter, Payload, Peer, Stream, STATE}; pub(super) static CLIENTS: RoCell>> = RoCell::new(); @@ -44,7 +44,7 @@ impl Server { let Some(id) = id else { continue }; if line.starts_with("bye,") { - writer.write_all(BodyBye::borrowed().with_receiver(id).with_sender(0).to_string().as_bytes()).await.ok(); + Self::handle_bye(id, rx, writer).await; break; } @@ -77,7 +77,11 @@ impl Server { else => break } } - Self::handle_bye(id); + + let mut clients = CLIENTS.write(); + if id.and_then(|id| clients.remove(&id)).is_some() { + Self::handle_hey(&clients); + } }); } })) @@ -111,18 +115,24 @@ impl Server { fn handle_hey(clients: &HashMap) { let payload = format!( "{}\n", - Payload::new( - BodyHey { peers: clients.values().map(|c| (c.id, Peer::new(&c.abilities))).collect() } - .into() - ) + Payload::new(BodyHey::owned( + clients.values().map(|c| (c.id, Peer::new(&c.abilities))).collect() + )) ); clients.values().for_each(|c| _ = c.tx.send(payload.clone())); } - fn handle_bye(id: Option) { - let mut clients = CLIENTS.write(); - if id.and_then(|id| clients.remove(&id)).is_some() { - Self::handle_hey(&clients); + async fn handle_bye(id: u64, mut rx: UnboundedReceiver, mut writer: ClientWriter) { + while let Ok(payload) = rx.try_recv() { + if writer.write_all(payload.as_bytes()).await.is_err() { + break; + } } + + _ = writer + .write_all(BodyBye::owned().with_receiver(id).with_sender(0).to_string().as_bytes()) + .await; + + writer.flush().await.ok(); } } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 955128bb8..494f0b33c 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -32,7 +32,7 @@ mlua = { version = "0.9.8", features = [ "lua54" ] } ratatui = "0.26.3" scopeguard = "1.2.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-util = "0.7.11" # Logging diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 50da887f3..09da959b7 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -30,12 +30,12 @@ md-5 = "0.10.6" mlua = { version = "0.9.8", features = [ "lua54", "serialize", "macros", "async" ] } parking_lot = "0.12.3" ratatui = "0.26.3" -serde = "1.0.202" +serde = "1.0.203" serde_json = "1.0.117" shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 041828a44..bb69bf2df 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -19,4 +19,4 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" mlua = { version = "0.9.8", features = [ "lua54" ] } -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index 290c9ab67..77e43b3b8 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -21,7 +21,7 @@ async-priority-channel = "0.2.0" futures = "0.3.30" parking_lot = "0.12.3" scopeguard = "1.2.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 37337d105..ed3391f69 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -20,9 +20,9 @@ parking_lot = "0.12.3" percent-encoding = "2.3.1" ratatui = "0.26.3" regex = "1.10.4" -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] }