From 54687181b3d312481d2eae201656b2765804543a 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: Mon, 28 Apr 2025 23:52:23 +0800 Subject: [PATCH] feat: promote `Id` to a first-class type (#2692) --- {yazi-plugin => yazi-binding}/src/id.rs | 0 yazi-binding/src/lib.rs | 2 +- yazi-core/src/cmp/cmp.rs | 4 +++- yazi-core/src/cmp/commands/show.rs | 6 +++--- yazi-core/src/cmp/commands/trigger.rs | 6 +++--- yazi-dds/src/sendable.rs | 18 ++++++------------ yazi-fm/src/lives/tab.rs | 3 +-- yazi-plugin/src/fs/op.rs | 4 ++-- yazi-plugin/src/lib.rs | 2 +- yazi-plugin/src/pubsub/pubsub.rs | 3 ++- yazi-plugin/src/utils/app.rs | 3 ++- yazi-proxy/src/input.rs | 4 ++-- yazi-proxy/src/tab.rs | 4 ++-- yazi-shared/src/event/cmd.rs | 8 ++++---- yazi-shared/src/event/data.rs | 18 +++++++++++++++--- yazi-shared/src/id.rs | 12 +++++++++++- yazi-widgets/src/input/commands/complete.rs | 6 +++--- 17 files changed, 61 insertions(+), 42 deletions(-) rename {yazi-plugin => yazi-binding}/src/id.rs (100%) diff --git a/yazi-plugin/src/id.rs b/yazi-binding/src/id.rs similarity index 100% rename from yazi-plugin/src/id.rs rename to yazi-binding/src/id.rs diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index c9534e84..68afe73e 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -1,3 +1,3 @@ mod macros; -yazi_macro::mod_flat!(error stage url); +yazi_macro::mod_flat!(error id stage url); diff --git a/yazi-core/src/cmp/cmp.rs b/yazi-core/src/cmp/cmp.rs index 7e799a8b..ec463006 100644 --- a/yazi-core/src/cmp/cmp.rs +++ b/yazi-core/src/cmp/cmp.rs @@ -1,5 +1,7 @@ use std::{collections::HashMap, path::PathBuf}; +use yazi_shared::Id; + #[derive(Default)] pub struct Cmp { pub(super) caches: HashMap>, @@ -7,7 +9,7 @@ pub struct Cmp { pub(super) offset: usize, pub cursor: usize, - pub(super) ticket: usize, + pub(super) ticket: Id, pub visible: bool, } diff --git a/yazi-core/src/cmp/commands/show.rs b/yazi-core/src/cmp/commands/show.rs index be328893..987e1849 100644 --- a/yazi-core/src/cmp/commands/show.rs +++ b/yazi-core/src/cmp/commands/show.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, mem, ops::ControlFlow, path::PathBuf}; use yazi_macro::render; -use yazi_shared::event::{Cmd, CmdCow, Data}; +use yazi_shared::{Id, event::{Cmd, CmdCow, Data}}; use crate::cmp::Cmp; @@ -11,7 +11,7 @@ struct Opt { cache: Vec, cache_name: PathBuf, word: Cow<'static, str>, - ticket: usize, + ticket: Id, } impl From for Opt { @@ -20,7 +20,7 @@ impl From for Opt { cache: c.take_any("cache").unwrap_or_default(), cache_name: c.take_any("cache-name").unwrap_or_default(), word: c.take_str("word").unwrap_or_default(), - ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0), + ticket: c.get("ticket").and_then(Data::as_id).unwrap_or_default(), } } } diff --git a/yazi-core/src/cmp/commands/trigger.rs b/yazi-core/src/cmp/commands/trigger.rs index 49203df3..19dd3c81 100644 --- a/yazi-core/src/cmp/commands/trigger.rs +++ b/yazi-core/src/cmp/commands/trigger.rs @@ -3,20 +3,20 @@ use std::{borrow::Cow, mem, path::{MAIN_SEPARATOR_STR, PathBuf}}; use tokio::fs; use yazi_fs::{CWD, expand_path}; use yazi_macro::{emit, render}; -use yazi_shared::event::{Cmd, CmdCow, Data}; +use yazi_shared::{Id, event::{Cmd, CmdCow, Data}}; use crate::cmp::Cmp; struct Opt { word: Cow<'static, str>, - ticket: usize, + ticket: Id, } impl From for Opt { fn from(mut c: CmdCow) -> Self { Self { word: c.take_first_str().unwrap_or_default(), - ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0), + ticket: c.get("ticket").and_then(Data::as_id).unwrap_or_default(), } } } diff --git a/yazi-dds/src/sendable.rs b/yazi-dds/src/sendable.rs index 0d226d99..4017151a 100644 --- a/yazi-dds/src/sendable.rs +++ b/yazi-dds/src/sendable.rs @@ -43,6 +43,8 @@ impl Sendable { Value::UserData(ud) => { if let Ok(t) = ud.take::() { Data::Url(t.into()) + } else if let Ok(t) = ud.borrow::() { + Data::Id(**t) } else if let Ok(t) = ud.take::() { Data::Any(Box::new(t)) } else if let Ok(t) = ud.take::() { @@ -107,6 +109,7 @@ impl Sendable { } Value::Table(tbl) } + Data::Id(i) => yazi_binding::Id(*i).into_lua(lua)?, Data::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?, Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Any(a) => Value::UserData(if let Some(t) = a.downcast_ref::() { @@ -188,23 +191,15 @@ impl Sendable { Value::Table(_) => Err("table is not supported".into_lua_err())?, Value::Function(_) => Err("function is not supported".into_lua_err())?, Value::Thread(_) => Err("thread is not supported".into_lua_err())?, - Value::UserData(ud) => { - if let Ok(t) = ud.take::() { - DataKey::Url(t.into()) - } else { - Err("unsupported userdata included".into_lua_err())? - } - } + Value::UserData(_) => Err("userdata is not supported".into_lua_err())?, Value::Error(_) => Err("error is not supported".into_lua_err())?, Value::Other(..) => Err("unknown data is not supported".into_lua_err())?, }) } + #[inline] fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result { - Ok(match key { - DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, - key => Self::key_to_value_ref(lua, &key)?, - }) + Self::key_to_value_ref(lua, &key) } fn key_to_value_ref(lua: &Lua, key: &DataKey) -> mlua::Result { @@ -214,7 +209,6 @@ impl Sendable { DataKey::Integer(i) => Value::Integer(*i), DataKey::Number(n) => Value::Number(n.get()), DataKey::String(s) => Value::String(lua.create_string(s.as_ref())?), - DataKey::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?, }) } } diff --git a/yazi-fm/src/lives/tab.rs b/yazi-fm/src/lives/tab.rs index 067b9a50..b22c5821 100644 --- a/yazi-fm/src/lives/tab.rs +++ b/yazi-fm/src/lives/tab.rs @@ -1,8 +1,7 @@ use std::ops::Deref; use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value}; -use yazi_binding::{UrlRef, cached_field}; -use yazi_plugin::Id; +use yazi_binding::{Id, UrlRef, cached_field}; use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected}; diff --git a/yazi-plugin/src/fs/op.rs b/yazi-plugin/src/fs/op.rs index f8e572d6..e312eba5 100644 --- a/yazi-plugin/src/fs/op.rs +++ b/yazi-plugin/src/fs/op.rs @@ -1,7 +1,7 @@ use mlua::{IntoLua, Lua, Table}; -use yazi_binding::Url; +use yazi_binding::{Id, Url}; -use crate::{Id, bindings::Cha, file::File}; +use crate::{bindings::Cha, file::File}; pub(super) struct FilesOp(yazi_fs::FilesOp); diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index 4a710d44..a254bff2 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -4,7 +4,7 @@ mod macros; yazi_macro::mod_pub!(bindings config elements external file fs isolate loader process pubsub utils); -yazi_macro::mod_flat!(clipboard composer id lua runtime); +yazi_macro::mod_flat!(clipboard composer lua runtime); pub fn init() -> anyhow::Result<()> { CLIPBOARD.with(<_>::default); diff --git a/yazi-plugin/src/pubsub/pubsub.rs b/yazi-plugin/src/pubsub/pubsub.rs index 319528bd..dec65256 100644 --- a/yazi-plugin/src/pubsub/pubsub.rs +++ b/yazi-plugin/src/pubsub/pubsub.rs @@ -1,7 +1,8 @@ use mlua::{ExternalResult, Function, Lua, Value}; +use yazi_binding::Id; use yazi_dds::body::Body; -use crate::{Id, runtime::RtRef}; +use crate::runtime::RtRef; pub struct Pubsub; diff --git a/yazi-plugin/src/utils/app.rs b/yazi-plugin/src/utils/app.rs index 0d8f9917..f4548601 100644 --- a/yazi-plugin/src/utils/app.rs +++ b/yazi-plugin/src/utils/app.rs @@ -1,8 +1,9 @@ use mlua::{AnyUserData, ExternalError, Function, Lua}; +use yazi_binding::Id; use yazi_proxy::{AppProxy, HIDER}; use super::Utils; -use crate::{Id, bindings::{Permit, PermitRef}}; +use crate::bindings::{Permit, PermitRef}; impl Utils { pub(super) fn id(lua: &Lua) -> mlua::Result { diff --git a/yazi-proxy/src/input.rs b/yazi-proxy/src/input.rs index 187b56b4..bdbbcf28 100644 --- a/yazi-proxy/src/input.rs +++ b/yazi-proxy/src/input.rs @@ -1,7 +1,7 @@ use tokio::sync::mpsc; use yazi_config::popup::InputCfg; use yazi_macro::emit; -use yazi_shared::{errors::InputError, event::Cmd}; +use yazi_shared::{Id, errors::InputError, event::Cmd}; pub struct InputProxy; @@ -14,7 +14,7 @@ impl InputProxy { } #[inline] - pub fn complete(word: &str, ticket: usize) { + pub fn complete(word: &str, ticket: Id) { emit!(Call(Cmd::args("input:complete", &[word]).with("ticket", ticket))); } } diff --git a/yazi-proxy/src/tab.rs b/yazi-proxy/src/tab.rs index cf7fee1c..ef70941d 100644 --- a/yazi-proxy/src/tab.rs +++ b/yazi-proxy/src/tab.rs @@ -26,8 +26,8 @@ impl TabProxy { emit!(Call( // TODO: use second positional argument instead of `args` parameter Cmd::args("mgr:search_do", &[opt.subject]) - .with("via", opt.via.as_ref()) - .with("args", opt.args_raw) + .with("via", opt.via.as_ref().to_owned()) + .with("args", opt.args_raw.into_owned()) )); } } diff --git a/yazi-shared/src/event/cmd.rs b/yazi-shared/src/event/cmd.rs index 933ae51e..9534ee84 100644 --- a/yazi-shared/src/event/cmd.rs +++ b/yazi-shared/src/event/cmd.rs @@ -46,15 +46,15 @@ impl Cmd { // --- With #[inline] - pub fn with(mut self, name: impl Into, value: impl ToString) -> Self { - self.args.insert(name.into(), Data::String(value.to_string())); + pub fn with(mut self, name: impl Into, value: impl Into) -> Self { + self.args.insert(name.into(), value.into()); self } #[inline] - pub fn with_opt(mut self, name: impl Into, value: Option) -> Self { + pub fn with_opt(mut self, name: impl Into, value: Option>) -> Self { if let Some(v) = value { - self.args.insert(name.into(), Data::String(v.to_string())); + self.args.insert(name.into(), v.into()); } self } diff --git a/yazi-shared/src/event/data.rs b/yazi-shared/src/event/data.rs index 1eab3f7b..3509d7fe 100644 --- a/yazi-shared/src/event/data.rs +++ b/yazi-shared/src/event/data.rs @@ -2,7 +2,7 @@ use std::{any::Any, borrow::Cow, collections::HashMap}; use serde::{Deserialize, Serialize, de}; -use crate::{OrderedFloat, url::Url}; +use crate::{Id, OrderedFloat, url::Url}; // --- Data #[derive(Debug, Serialize, Deserialize)] @@ -15,6 +15,7 @@ pub enum Data { String(String), List(Vec), Dict(HashMap), + Id(Id), #[serde(skip_deserializing)] Url(Url), #[serde(skip)] @@ -83,6 +84,18 @@ impl Data { } } +impl From for Data { + fn from(value: usize) -> Self { Self::Id(value.into()) } +} + +impl From for Data { + fn from(value: String) -> Self { Self::String(value) } +} + +impl From for Data { + fn from(value: Id) -> Self { Self::Id(value) } +} + // --- Key #[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] @@ -93,8 +106,6 @@ pub enum DataKey { Integer(i64), Number(OrderedFloat), String(Cow<'static, str>), - #[serde(skip_deserializing)] - Url(Url), } impl DataKey { @@ -157,6 +168,7 @@ macro_rules! impl_integer_as { match self { Data::Integer(i) => <$t>::try_from(*i).ok(), Data::String(s) => s.parse().ok(), + Data::Id(i) => <$t>::try_from(i.get()).ok(), _ => None, } } diff --git a/yazi-shared/src/id.rs b/yazi-shared/src/id.rs index 3b68f850..ffb67b2e 100644 --- a/yazi-shared/src/id.rs +++ b/yazi-shared/src/id.rs @@ -2,7 +2,9 @@ use std::{fmt::Display, str::FromStr, sync::atomic::{AtomicU64, Ordering}}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize, +)] pub struct Id(pub u64); impl Id { @@ -22,6 +24,14 @@ impl FromStr for Id { fn from_str(s: &str) -> Result { s.parse().map(Self) } } +impl From for Id { + fn from(value: u64) -> Self { Self(value) } +} + +impl From for Id { + fn from(value: usize) -> Self { Self(value as u64) } +} + impl TryFrom for Id { type Error = >::Error; diff --git a/yazi-widgets/src/input/commands/complete.rs b/yazi-widgets/src/input/commands/complete.rs index 241a3e03..9d123115 100644 --- a/yazi-widgets/src/input/commands/complete.rs +++ b/yazi-widgets/src/input/commands/complete.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, path::MAIN_SEPARATOR_STR}; use yazi_macro::render; -use yazi_shared::event::{CmdCow, Data}; +use yazi_shared::{Id, event::{CmdCow, Data}}; use crate::input::Input; @@ -13,14 +13,14 @@ const SEPARATOR: char = std::path::MAIN_SEPARATOR; struct Opt { word: Cow<'static, str>, - _ticket: usize, // FIXME: not used + _ticket: Id, // FIXME: not used } impl From for Opt { fn from(mut c: CmdCow) -> Self { Self { word: c.take_first_str().unwrap_or_default(), - _ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0), + _ticket: c.get("ticket").and_then(Data::as_id).unwrap_or_default(), } } }