mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: hover cursor over the new file after extracting (#3854)
This commit is contained in:
parent
ec178fdb52
commit
354cdef907
52 changed files with 979 additions and 321 deletions
|
|
@ -16,7 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
|
||||
- Custom tab name ([#3666])
|
||||
- New `--in` for `search` action to set search directory ([#3696])
|
||||
- Hover cursor over the new file after copying/cutting/linking/hardlinking ([#3846])
|
||||
- Hover cursor over the new file after copying/cutting/linking/hardlinking/extracting ([#3846], [#3854])
|
||||
- Multi-file spotter ([#3733])
|
||||
- Vim-like `lua` action that runs an inline Lua snippet ([#3813])
|
||||
- Certificate authentication for SFTP VFS provider ([#3716])
|
||||
|
|
@ -1696,3 +1696,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
[#3804]: https://github.com/sxyazi/yazi/pull/3804
|
||||
[#3813]: https://github.com/sxyazi/yazi/pull/3813
|
||||
[#3846]: https://github.com/sxyazi/yazi/pull/3846
|
||||
[#3854]: https://github.com/sxyazi/yazi/pull/3854
|
||||
|
|
|
|||
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -6102,6 +6102,7 @@ dependencies = [
|
|||
"paste",
|
||||
"ratatui",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"strum 0.28.0",
|
||||
"tokio",
|
||||
"yazi-binding",
|
||||
|
|
@ -6111,7 +6112,6 @@ dependencies = [
|
|||
"yazi-dds",
|
||||
"yazi-fs",
|
||||
"yazi-macro",
|
||||
"yazi-runner",
|
||||
"yazi-scheduler",
|
||||
"yazi-shared",
|
||||
"yazi-vfs",
|
||||
|
|
@ -6165,11 +6165,11 @@ checksum = "33232d9116df6415ddfcdf72701b1b7439ce87f240a14723e8dd5d17e7ed5f98"
|
|||
name = "yazi-proxy"
|
||||
version = "26.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"tokio",
|
||||
"yazi-config",
|
||||
"yazi-core",
|
||||
"yazi-macro",
|
||||
"yazi-runner",
|
||||
"yazi-scheduler",
|
||||
"yazi-shared",
|
||||
"yazi-widgets",
|
||||
|
|
@ -6180,12 +6180,9 @@ name = "yazi-runner"
|
|||
version = "26.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dyn-clone",
|
||||
"hashbrown 0.16.1",
|
||||
"mlua",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"strum 0.28.0",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
|
@ -6215,7 +6212,6 @@ dependencies = [
|
|||
"serde",
|
||||
"strum 0.28.0",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"yazi-binding",
|
||||
"yazi-config",
|
||||
|
|
@ -6360,6 +6356,7 @@ dependencies = [
|
|||
"mlua",
|
||||
"parking_lot",
|
||||
"ratatui",
|
||||
"serde",
|
||||
"tokio",
|
||||
"unicode-width",
|
||||
"yazi-adapter",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl Actor for Plugin {
|
|||
}
|
||||
|
||||
if opt.mode == PluginMode::Async {
|
||||
succ!(cx.core.tasks.scheduler.plugin_entry(opt.id, opt.args));
|
||||
succ!(cx.core.tasks.scheduler.plugin_entry(opt.into()));
|
||||
} else if opt.mode == PluginMode::Sync && hits {
|
||||
return act!(app:plugin_do, cx, opt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl Actor for PluginDo {
|
|||
}
|
||||
|
||||
if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync {
|
||||
succ!(cx.core.tasks.scheduler.plugin_entry(opt.id, opt.args));
|
||||
succ!(cx.core.tasks.scheduler.plugin_entry(opt.into()));
|
||||
}
|
||||
|
||||
let blocking = runtime_mut!(LUA)?.critical_push(&opt.id, true);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use super::{Lives, PtrCell};
|
|||
pub(super) struct TaskSnap {
|
||||
inner: PtrCell<yazi_scheduler::TaskSnap>,
|
||||
|
||||
v_name: Option<Value>,
|
||||
v_prog: Option<Value>,
|
||||
v_title: Option<Value>,
|
||||
v_prog: Option<Value>,
|
||||
}
|
||||
|
||||
impl Deref for TaskSnap {
|
||||
|
|
@ -21,13 +21,13 @@ impl Deref for TaskSnap {
|
|||
|
||||
impl TaskSnap {
|
||||
pub(super) fn make(inner: &yazi_scheduler::TaskSnap) -> mlua::Result<AnyUserData> {
|
||||
Lives::scoped_userdata(Self { inner: inner.into(), v_name: None, v_prog: None })
|
||||
Lives::scoped_userdata(Self { inner: inner.into(), v_title: None, v_prog: None })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for TaskSnap {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, name, |lua, me| lua.create_string(&me.name));
|
||||
cached_field!(fields, title, |lua, me| lua.create_string(&me.title));
|
||||
cached_field!(fields, prog, |lua, me| lua.to_value_with(&me.prog, SER_OPT));
|
||||
|
||||
fields.add_field_method_get("running", |_, me| Ok(me.prog.running()));
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ impl Actor for Quit {
|
|||
|
||||
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
|
||||
let ongoing = cx.tasks.scheduler.ongoing.clone();
|
||||
let (left, left_names) = {
|
||||
let (left, left_titles) = {
|
||||
let ongoing = ongoing.lock();
|
||||
(ongoing.len(), ongoing.values().take(11).map(|t| t.name.clone()).collect())
|
||||
(ongoing.len(), ongoing.values().take(11).map(|t| t.title.clone()).collect())
|
||||
};
|
||||
|
||||
if left == 0 {
|
||||
|
|
@ -31,7 +31,7 @@ impl Actor for Quit {
|
|||
|
||||
tokio::spawn(async move {
|
||||
let mut i = 0;
|
||||
let token = ConfirmProxy::show_sync(ConfirmCfg::quit(left, left_names));
|
||||
let token = ConfirmProxy::show_sync(ConfirmCfg::quit(left, left_titles));
|
||||
loop {
|
||||
select! {
|
||||
_ = time::sleep(Duration::from_millis(50)) => {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(arrow cancel close open_shell_compat inspect process_open show update_succeed);
|
||||
yazi_macro::mod_flat!(arrow cancel close inspect open_shell_compat process_open show spawn update_succeed);
|
||||
|
|
|
|||
21
yazi-actor/src/tasks/spawn.rs
Normal file
21
yazi-actor/src/tasks/spawn.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use anyhow::Result;
|
||||
use yazi_core::tasks::TaskOpt;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::tasks::SpawnForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Spawn;
|
||||
|
||||
impl Actor for Spawn {
|
||||
type Form = SpawnForm;
|
||||
|
||||
const NAME: &str = "spawn";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
succ!(match form.opt {
|
||||
TaskOpt::Plugin(r#in) => cx.tasks.scheduler.plugin_entry(r#in),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use hashbrown::HashMap;
|
|||
use mlua::{Lua, Table};
|
||||
use serde::Deserialize;
|
||||
use strum::EnumString;
|
||||
use yazi_scheduler::plugin::PluginInEntry;
|
||||
use yazi_shared::{SStr, data::{Data, DataKey}, event::{Action, ActionCow}};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
|
@ -36,6 +37,12 @@ impl TryFrom<ActionCow> for PluginOpt {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<PluginOpt> for PluginInEntry {
|
||||
fn from(value: PluginOpt) -> Self {
|
||||
Self { plugin: value.id, args: value.args, ..Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginOpt {
|
||||
pub fn new_callback(id: impl Into<SStr>, f: impl PluginCallback) -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
yazi_macro::mod_flat!(file prework process tasks);
|
||||
yazi_macro::mod_flat!(file option prework process tasks);
|
||||
|
||||
pub const TASKS_BORDER: u16 = 2;
|
||||
pub const TASKS_PADDING: u16 = 2;
|
||||
|
|
|
|||
6
yazi-core/src/tasks/option.rs
Normal file
6
yazi-core/src/tasks/option.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
use yazi_scheduler::plugin::PluginInEntry;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TaskOpt {
|
||||
Plugin(PluginInEntry),
|
||||
}
|
||||
|
|
@ -175,6 +175,7 @@ impl<'a> Executor<'a> {
|
|||
}
|
||||
|
||||
on!(update_succeed);
|
||||
on!(spawn);
|
||||
|
||||
on!(show);
|
||||
on!(close);
|
||||
|
|
|
|||
|
|
@ -24,23 +24,23 @@ yazi-core = { path = "../yazi-core", version = "26.2.2" }
|
|||
yazi-dds = { path = "../yazi-dds", version = "26.2.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "26.2.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "26.2.2" }
|
||||
yazi-runner = { path = "../yazi-runner", version = "26.2.2" }
|
||||
yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
||||
yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" }
|
||||
yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
ratatui = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
ratatui = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_with = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{ExternalError, IntoLua, Lua, Value};
|
||||
use serde::Deserialize;
|
||||
use yazi_shared::event::ActionCow;
|
||||
use yazi_widgets::Step;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
||||
pub struct ArrowForm {
|
||||
#[serde(alias = "0")]
|
||||
pub step: Step,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for ArrowForm {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(a: ActionCow) -> Result<Self, Self::Error> {
|
||||
let Ok(step) = a.first() else {
|
||||
bail!("Invalid 'step' in ArrowForm");
|
||||
};
|
||||
|
||||
Ok(Self { step })
|
||||
}
|
||||
fn try_from(a: ActionCow) -> Result<Self, Self::Error> { Ok(a.deserialize()?) }
|
||||
}
|
||||
|
||||
impl From<isize> for ArrowForm {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_binding::SER_OPT;
|
||||
|
|
@ -7,15 +5,14 @@ use yazi_shared::event::ActionCow;
|
|||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct HiddenForm {
|
||||
#[serde(alias = "0")]
|
||||
pub state: HiddenFormState,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for HiddenForm {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(a: ActionCow) -> Result<Self, Self::Error> {
|
||||
Ok(Self { state: a.str(0).parse().unwrap_or_default() })
|
||||
}
|
||||
fn try_from(a: ActionCow) -> Result<Self, Self::Error> { Ok(a.deserialize()?) }
|
||||
}
|
||||
|
||||
impl FromLua for HiddenForm {
|
||||
|
|
@ -37,14 +34,6 @@ pub enum HiddenFormState {
|
|||
Toggle,
|
||||
}
|
||||
|
||||
impl FromStr for HiddenFormState {
|
||||
type Err = serde::de::value::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::deserialize(serde::de::value::StrDeserializer::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl HiddenFormState {
|
||||
pub fn bool(self, old: bool) -> bool {
|
||||
match self {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,22 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use anyhow::bail;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use serde::Deserialize;
|
||||
use serde_with::{DurationSecondsWithFrac, serde_as};
|
||||
use yazi_shared::event::ActionCow;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[serde_as]
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct TickForm {
|
||||
#[serde(alias = "0")]
|
||||
#[serde_as(as = "DurationSecondsWithFrac<f64>")]
|
||||
pub interval: Duration,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for TickForm {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(a: ActionCow) -> Result<Self, Self::Error> {
|
||||
let Ok(interval) = a.first() else {
|
||||
bail!("Invalid 'interval' in TickForm");
|
||||
};
|
||||
|
||||
if interval < 0.0 {
|
||||
bail!("'interval' must be non-negative in TickForm");
|
||||
}
|
||||
|
||||
Ok(Self { interval: Duration::from_secs_f64(interval) })
|
||||
}
|
||||
fn try_from(a: ActionCow) -> Result<Self, Self::Error> { Ok(a.deserialize()?) }
|
||||
}
|
||||
|
||||
impl FromLua for TickForm {
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ pub enum Spark<'a> {
|
|||
TasksOpenShellCompat(crate::tasks::ProcessOpenForm),
|
||||
TasksProcessOpen(crate::tasks::ProcessOpenForm),
|
||||
TasksShow(crate::VoidForm),
|
||||
TasksSpawn(crate::tasks::SpawnForm),
|
||||
TasksUpdateSucceed(crate::tasks::UpdateSucceedForm),
|
||||
|
||||
// Which
|
||||
|
|
@ -331,6 +332,7 @@ impl<'a> IntoLua for Spark<'a> {
|
|||
Self::TasksOpenShellCompat(b) => b.into_lua(lua),
|
||||
Self::TasksProcessOpen(b) => b.into_lua(lua),
|
||||
Self::TasksShow(b) => b.into_lua(lua),
|
||||
Self::TasksSpawn(b) => b.into_lua(lua),
|
||||
Self::TasksUpdateSucceed(b) => b.into_lua(lua),
|
||||
|
||||
// Which
|
||||
|
|
@ -433,6 +435,7 @@ try_from_spark!(crate::pick::CloseForm, pick:close);
|
|||
try_from_spark!(crate::pick::ShowForm, pick:show);
|
||||
try_from_spark!(crate::spot::CopyForm, spot:copy);
|
||||
try_from_spark!(crate::tasks::ProcessOpenForm, tasks:process_open);
|
||||
try_from_spark!(crate::tasks::SpawnForm, tasks:spawn);
|
||||
try_from_spark!(crate::tasks::UpdateSucceedForm, tasks:update_succeed);
|
||||
try_from_spark!(crate::which::ActivateForm, which:activate);
|
||||
try_from_spark!(yazi_dds::Payload<'a>, app:accept_payload);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(process_open update_succeed);
|
||||
yazi_macro::mod_flat!(process_open spawn update_succeed);
|
||||
|
|
|
|||
25
yazi-parser/src/tasks/spawn.rs
Normal file
25
yazi-parser/src/tasks/spawn.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use anyhow::anyhow;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_core::tasks::TaskOpt;
|
||||
use yazi_shared::event::ActionCow;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SpawnForm {
|
||||
pub opt: TaskOpt,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for SpawnForm {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut a: ActionCow) -> Result<Self, Self::Error> {
|
||||
Ok(Self { opt: a.take_any("opt").ok_or_else(|| anyhow!("Invalid 'opt' in SpawnForm"))? })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for SpawnForm {
|
||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
||||
impl IntoLua for SpawnForm {
|
||||
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ function Tasks:redraw()
|
|||
break
|
||||
end
|
||||
|
||||
elements[#elements + 1] = ui.Line({ self:icon(snap), snap.name }):area(ui.Rect {
|
||||
elements[#elements + 1] = ui.Line({ self:icon(snap), snap.title }):area(ui.Rect {
|
||||
x = self._area.x,
|
||||
y = y,
|
||||
w = self._area.w,
|
||||
|
|
|
|||
|
|
@ -4,31 +4,48 @@ local M = {}
|
|||
|
||||
function M:setup()
|
||||
ps.sub_remote("extract", function(args)
|
||||
local noisy = #args == 1 and ' "" --noisy' or ' ""'
|
||||
for _, arg in ipairs(args) do
|
||||
ya.emit("plugin", { self._id, ya.quote(arg, true) .. noisy })
|
||||
end
|
||||
ya.async(function()
|
||||
for i, arg in ipairs(args) do
|
||||
ya.task("plugin", {
|
||||
self._id,
|
||||
args = { arg, "", noisy = #args == 1 },
|
||||
title = "Extract " .. arg,
|
||||
track = i == 1,
|
||||
}):spawn()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function M:entry(job)
|
||||
function M:init(job)
|
||||
local from = job.args[1] and Url(job.args[1])
|
||||
local to = job.args[2] ~= "" and Url(job.args[2]) or nil
|
||||
if not from then
|
||||
fail("No URL provided")
|
||||
end
|
||||
|
||||
local pwd = ""
|
||||
local to = job.args[2] ~= "" and Url(job.args[2]) or from.parent
|
||||
if not to then
|
||||
fail("Failed to determine target directory for '%s'", from)
|
||||
end
|
||||
|
||||
self.job = { id = job.id, from = from, to = to }
|
||||
end
|
||||
|
||||
function M:entry(job)
|
||||
self:init(job)
|
||||
|
||||
local pwd, target, retry = "", nil, false
|
||||
while true do
|
||||
if not M:try_with(from, pwd, to) then
|
||||
target, retry = self:try_with(pwd)
|
||||
if not retry then
|
||||
break
|
||||
elseif not job.args.noisy then
|
||||
fail("'%s' is password-protected, please extract it individually and enter the password", from)
|
||||
fail("'%s' is password-protected, please extract it individually and enter the password", self.job.from)
|
||||
end
|
||||
|
||||
local value, event = ya.input {
|
||||
pos = { "top-center", y = 2, w = 50 },
|
||||
title = string.format('Password for "%s":', from.name),
|
||||
title = string.format('Password for "%s":', self.job.from.name),
|
||||
obscure = true,
|
||||
}
|
||||
if event == 1 then
|
||||
|
|
@ -37,14 +54,14 @@ function M:entry(job)
|
|||
break
|
||||
end
|
||||
end
|
||||
|
||||
if target then
|
||||
ya.emit("tasks:update_succeed", { job.id, urls = { target }, track = true })
|
||||
end
|
||||
end
|
||||
|
||||
function M:try_with(from, pwd, to)
|
||||
to = to or from.parent
|
||||
if not to then
|
||||
fail("Invalid URL '%s'", from)
|
||||
end
|
||||
|
||||
function M:try_with(pwd)
|
||||
local from, to = self.job.from, self.job.to
|
||||
local tmp = fs.unique("dir", to:join(self.tmp_name(from)))
|
||||
if not tmp then
|
||||
fail("Failed to determine a temporary directory for %s", from)
|
||||
|
|
@ -59,18 +76,21 @@ function M:try_with(from, pwd, to)
|
|||
local output, err = child:wait_with_output()
|
||||
if output and output.status.code == 2 and archive.is_encrypted(output.stderr) then
|
||||
fs.remove("dir_all", tmp)
|
||||
return true -- Need to retry
|
||||
return nil, true -- Need to retry
|
||||
end
|
||||
|
||||
self:tidy(from, to, tmp)
|
||||
local target = self:tidy(tmp)
|
||||
if not output then
|
||||
fail("7zip failed to output when extracting '%s', error: %s", from, err)
|
||||
elseif output.status.code ~= 0 then
|
||||
fail("7zip exited with error code %s when extracting '%s':\n%s", output.status.code, from, output.stderr)
|
||||
else
|
||||
return target, false
|
||||
end
|
||||
end
|
||||
|
||||
function M:tidy(from, to, tmp)
|
||||
function M:tidy(tmp)
|
||||
local from, to = self.job.from, self.job.to
|
||||
local outs = fs.read_dir(tmp, { limit = 2 })
|
||||
if not outs then
|
||||
fail("Failed to read the temporary directory '%s' when extracting '%s'", tmp, from)
|
||||
|
|
@ -81,7 +101,7 @@ function M:tidy(from, to, tmp)
|
|||
|
||||
local only = #outs == 1 and outs[1]
|
||||
if only and not only.cha.is_dir and require("archive").is_tar(only.url) then
|
||||
self:entry { args = { tostring(only.url), tostring(to) } }
|
||||
self:entry { id = self.job.id, args = { tostring(only.url), tostring(to) } }
|
||||
fs.remove("file", only.url)
|
||||
fs.remove("dir", tmp)
|
||||
return
|
||||
|
|
@ -98,7 +118,9 @@ function M:tidy(from, to, tmp)
|
|||
elseif not only and not fs.rename(tmp, target) then
|
||||
fail('Failed to move "%s" to "%s"', tmp, target)
|
||||
end
|
||||
|
||||
fs.remove("dir", tmp)
|
||||
return target
|
||||
end
|
||||
|
||||
function M.tmp_name(url) return ".tmp_" .. ya.hash(string.format("extract//%s//%.10f", url, ya.time())) end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
yazi_macro::mod_pub!(elements external fs pubsub runtime theme utils);
|
||||
yazi_macro::mod_pub!(elements external fs pubsub runtime tasks theme utils);
|
||||
|
||||
yazi_macro::mod_flat!(slim standard);
|
||||
|
||||
|
|
|
|||
1
yazi-plugin/src/tasks/mod.rs
Normal file
1
yazi-plugin/src/tasks/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(option task);
|
||||
15
yazi-plugin/src/tasks/option.rs
Normal file
15
yazi-plugin/src/tasks/option.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
use mlua::{UserData, UserDataMethods};
|
||||
use yazi_proxy::TasksProxy;
|
||||
|
||||
use crate::tasks::Task;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct TaskOpt(pub(crate) yazi_core::tasks::TaskOpt);
|
||||
|
||||
impl UserData for TaskOpt {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_async_method_once("spawn", |_, me, ()| async move {
|
||||
Ok(Task { id: TasksProxy::spawn(me.0).await? })
|
||||
});
|
||||
}
|
||||
}
|
||||
13
yazi-plugin/src/tasks/task.rs
Normal file
13
yazi-plugin/src/tasks/task.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
use mlua::{UserData, UserDataFields};
|
||||
use yazi_shared::Id;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Task {
|
||||
pub(super) id: Id,
|
||||
}
|
||||
|
||||
impl UserData for Task {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("id", |_, me| Ok(yazi_binding::Id(me.id)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
yazi_macro::mod_flat!(
|
||||
app cache call image json layer log preview process spot sync target text time user utils
|
||||
app cache call image json layer log preview process spot sync target tasks text time user utils
|
||||
);
|
||||
|
|
|
|||
16
yazi-plugin/src/utils/tasks.rs
Normal file
16
yazi-plugin/src/utils/tasks.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use mlua::{ExternalError, FromLua, Function, Lua, Value};
|
||||
use yazi_core::tasks;
|
||||
|
||||
use super::Utils;
|
||||
use crate::tasks::TaskOpt;
|
||||
|
||||
impl Utils {
|
||||
pub(super) fn task(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, (kind, value): (mlua::String, Value)| {
|
||||
Ok(TaskOpt(match &*kind.as_bytes() {
|
||||
b"plugin" => tasks::TaskOpt::Plugin(<_>::from_lua(value, lua)?),
|
||||
_ => Err(format!("unsupported spawn kind: {}", kind.display()).into_lua_err())?,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +82,9 @@ pub fn compose(
|
|||
#[cfg(unix)]
|
||||
b"host_name" => Utils::host_name(lua)?,
|
||||
|
||||
// Task
|
||||
b"task" => Utils::task(lua)?,
|
||||
|
||||
_ => return Ok(Value::Nil),
|
||||
}
|
||||
.into_lua(lua)
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ workspace = true
|
|||
yazi-config = { path = "../yazi-config", version = "26.2.2" }
|
||||
yazi-core = { path = "../yazi-core", version = "26.2.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "26.2.2" }
|
||||
yazi-runner = { path = "../yazi-runner", version = "26.2.2" }
|
||||
yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
||||
yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" }
|
||||
|
||||
# External dependencies
|
||||
tokio = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_core::tasks::TaskOpt;
|
||||
use yazi_macro::{emit, relay};
|
||||
use yazi_scheduler::process::ProcessOpt;
|
||||
use yazi_shared::url::UrlCow;
|
||||
use yazi_shared::{Id, url::UrlCow};
|
||||
|
||||
pub struct TasksProxy;
|
||||
|
||||
impl TasksProxy {
|
||||
pub async fn spawn(opt: TaskOpt) -> Result<Id> {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
emit!(Call(relay!(tasks:spawn).with_any("opt", opt).with_replier(tx)));
|
||||
|
||||
rx.recv().await.ok_or_else(|| anyhow!("channel closed"))??.try_into()
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
pub fn open_shell_compat(opt: ProcessOpt) {
|
||||
emit!(Call(relay!(tasks:open_shell_compat).with_any("opt", opt)));
|
||||
|
|
|
|||
|
|
@ -23,12 +23,9 @@ yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
|||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
dyn-clone = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-util = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ parking_lot = { workspace = true }
|
|||
serde = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-util = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl Fetch {
|
|||
loaded.get_mut(&h).map(|x| *x &= !(1 << plugin.idx));
|
||||
}
|
||||
if let Some(e) = err {
|
||||
error!("Error when running fetcher `{}`:\n{e}", plugin.run.name);
|
||||
error!("Error when running fetcher '{}':\n{e}", plugin.run.name);
|
||||
}
|
||||
|
||||
Ok(self.ops.out(id, FetchOutFetch::Succ))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use yazi_config::plugin::Fetcher;
|
||||
use yazi_runner::fetcher::FetchJob;
|
||||
use yazi_shared::Id;
|
||||
|
||||
use crate::{TaskIn, fetch::FetchProg};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FetchIn {
|
||||
pub(crate) id: Id,
|
||||
|
|
@ -9,10 +13,21 @@ pub(crate) struct FetchIn {
|
|||
pub(crate) targets: Vec<yazi_fs::File>,
|
||||
}
|
||||
|
||||
impl TaskIn for FetchIn {
|
||||
type Prog = FetchProg;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Run fetcher '{}' with {} target(s)", self.plugin.run.name, self.targets.len()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FetchIn> for FetchJob {
|
||||
fn from(value: FetchIn) -> Self { Self { action: &value.plugin.run, files: value.targets } }
|
||||
}
|
||||
|
||||
impl FetchIn {
|
||||
pub(crate) fn id(&self) -> Id { self.id }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use std::{mem, path::PathBuf};
|
||||
use std::{borrow::Cow, mem, path::PathBuf};
|
||||
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_fs::cha::Cha;
|
||||
use yazi_shared::{Id, url::UrlBuf};
|
||||
use yazi_shared::{Id, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use crate::{TaskIn, file::{FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum FileIn {
|
||||
|
|
@ -24,6 +26,74 @@ pub(crate) enum FileIn {
|
|||
UploadDo(FileInUpload),
|
||||
}
|
||||
|
||||
impl TaskIn for FileIn {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id {
|
||||
match self {
|
||||
Self::Copy(r#in) => r#in.id(),
|
||||
Self::CopyDo(r#in) => r#in.id(),
|
||||
Self::Cut(r#in) => r#in.id(),
|
||||
Self::CutDo(r#in) => r#in.id(),
|
||||
Self::Link(r#in) => r#in.id(),
|
||||
Self::LinkDo(r#in) => r#in.id(),
|
||||
Self::Hardlink(r#in) => r#in.id(),
|
||||
Self::HardlinkDo(r#in) => r#in.id(),
|
||||
Self::Delete(r#in) => r#in.id(),
|
||||
Self::DeleteDo(r#in) => r#in.id(),
|
||||
Self::Trash(r#in) => r#in.id(),
|
||||
Self::TrashDo(r#in) => r#in.id(),
|
||||
Self::Download(r#in) => r#in.id(),
|
||||
Self::DownloadDo(r#in) => r#in.id(),
|
||||
Self::Upload(r#in) => r#in.id(),
|
||||
Self::UploadDo(r#in) => r#in.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
match self {
|
||||
Self::Copy(r#in) => _ = r#in.with_id(id),
|
||||
Self::CopyDo(r#in) => _ = r#in.with_id(id),
|
||||
Self::Cut(r#in) => _ = r#in.with_id(id),
|
||||
Self::CutDo(r#in) => _ = r#in.with_id(id),
|
||||
Self::Link(r#in) => _ = r#in.with_id(id),
|
||||
Self::LinkDo(r#in) => _ = r#in.with_id(id),
|
||||
Self::Hardlink(r#in) => _ = r#in.with_id(id),
|
||||
Self::HardlinkDo(r#in) => _ = r#in.with_id(id),
|
||||
Self::Delete(r#in) => _ = r#in.with_id(id),
|
||||
Self::DeleteDo(r#in) => _ = r#in.with_id(id),
|
||||
Self::Trash(r#in) => _ = r#in.with_id(id),
|
||||
Self::TrashDo(r#in) => _ = r#in.with_id(id),
|
||||
Self::Download(r#in) => _ = r#in.with_id(id),
|
||||
Self::DownloadDo(r#in) => _ = r#in.with_id(id),
|
||||
Self::Upload(r#in) => _ = r#in.with_id(id),
|
||||
Self::UploadDo(r#in) => _ = r#in.with_id(id),
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
Self::Copy(r#in) => r#in.title(),
|
||||
Self::CopyDo(r#in) => r#in.title(),
|
||||
Self::Cut(r#in) => r#in.title(),
|
||||
Self::CutDo(r#in) => r#in.title(),
|
||||
Self::Link(r#in) => r#in.title(),
|
||||
Self::LinkDo(r#in) => r#in.title(),
|
||||
Self::Hardlink(r#in) => r#in.title(),
|
||||
Self::HardlinkDo(r#in) => r#in.title(),
|
||||
Self::Delete(r#in) => r#in.title(),
|
||||
Self::DeleteDo(r#in) => r#in.title(),
|
||||
Self::Trash(r#in) => r#in.title(),
|
||||
Self::TrashDo(r#in) => r#in.title(),
|
||||
Self::Download(r#in) => r#in.title(),
|
||||
Self::DownloadDo(r#in) => r#in.title(),
|
||||
Self::Upload(r#in) => r#in.title(),
|
||||
Self::UploadDo(r#in) => r#in.title(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_in! {
|
||||
Copy(FileInCopy),
|
||||
Cut(FileInCut),
|
||||
|
|
@ -36,27 +106,6 @@ impl_from_in! {
|
|||
}
|
||||
|
||||
impl FileIn {
|
||||
pub(crate) fn id(&self) -> Id {
|
||||
match self {
|
||||
Self::Copy(r#in) => r#in.id,
|
||||
Self::CopyDo(r#in) => r#in.id,
|
||||
Self::Cut(r#in) => r#in.id,
|
||||
Self::CutDo(r#in) => r#in.id,
|
||||
Self::Link(r#in) => r#in.id,
|
||||
Self::LinkDo(r#in) => r#in.id,
|
||||
Self::Hardlink(r#in) => r#in.id,
|
||||
Self::HardlinkDo(r#in) => r#in.id,
|
||||
Self::Delete(r#in) => r#in.id,
|
||||
Self::DeleteDo(r#in) => r#in.id,
|
||||
Self::Trash(r#in) => r#in.id,
|
||||
Self::TrashDo(r#in) => r#in.id,
|
||||
Self::Download(r#in) => r#in.id,
|
||||
Self::DownloadDo(r#in) => r#in.id,
|
||||
Self::Upload(r#in) => r#in.id,
|
||||
Self::UploadDo(r#in) => r#in.id,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_doable(self) -> Self {
|
||||
match self {
|
||||
Self::Copy(r#in) => Self::CopyDo(r#in),
|
||||
|
|
@ -91,6 +140,21 @@ pub(crate) struct FileInCopy {
|
|||
pub(crate) retry: u8,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInCopy {
|
||||
type Prog = FileProgCopy;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Copy {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileInCopy {
|
||||
pub(super) fn into_link(self) -> FileInLink {
|
||||
FileInLink {
|
||||
|
|
@ -119,6 +183,21 @@ pub(crate) struct FileInCut {
|
|||
pub(crate) drop: Option<mpsc::Sender<()>>,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInCut {
|
||||
type Prog = FileProgCut;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Cut {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileInCut {
|
||||
fn drop(&mut self) { _ = self.drop.take(); }
|
||||
}
|
||||
|
|
@ -156,6 +235,21 @@ pub(crate) struct FileInLink {
|
|||
pub(crate) delete: bool,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInLink {
|
||||
type Prog = FileProgLink;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Link {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
// --- Hardlink
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FileInHardlink {
|
||||
|
|
@ -167,6 +261,21 @@ pub(crate) struct FileInHardlink {
|
|||
pub(crate) follow: bool,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInHardlink {
|
||||
type Prog = FileProgHardlink;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Hardlink {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
// --- Delete
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FileInDelete {
|
||||
|
|
@ -175,6 +284,19 @@ pub(crate) struct FileInDelete {
|
|||
pub(crate) cha: Option<Cha>,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInDelete {
|
||||
type Prog = FileProgDelete;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Delete {}", self.target.display()).into() }
|
||||
}
|
||||
|
||||
// --- Trash
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FileInTrash {
|
||||
|
|
@ -182,6 +304,19 @@ pub(crate) struct FileInTrash {
|
|||
pub(crate) target: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInTrash {
|
||||
type Prog = FileProgTrash;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Trash {}", self.target.display()).into() }
|
||||
}
|
||||
|
||||
// --- Download
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FileInDownload {
|
||||
|
|
@ -191,6 +326,19 @@ pub(crate) struct FileInDownload {
|
|||
pub(crate) retry: u8,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInDownload {
|
||||
type Prog = FileProgDownload;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Download {}", self.target.display()).into() }
|
||||
}
|
||||
|
||||
// --- Upload
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FileInUpload {
|
||||
|
|
@ -199,3 +347,16 @@ pub(crate) struct FileInUpload {
|
|||
pub(crate) cha: Option<Cha>,
|
||||
pub(crate) cache: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl TaskIn for FileInUpload {
|
||||
type Prog = FileProgUpload;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Upload {}", self.target.display()).into() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use yazi_shared::{Id, url::UrlBuf};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{Task, TaskProg};
|
||||
use yazi_shared::{Id, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use crate::{Task, TaskIn, TaskProg};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum HookIn {
|
||||
|
|
@ -27,32 +29,49 @@ impl_from_in!(
|
|||
Preload(HookInPreload),
|
||||
);
|
||||
|
||||
impl HookIn {
|
||||
pub(crate) fn id(&self) -> Id {
|
||||
impl TaskIn for HookIn {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id {
|
||||
match self {
|
||||
Self::Copy(r#in) => r#in.id,
|
||||
Self::Cut(r#in) => r#in.id,
|
||||
Self::Delete(r#in) => r#in.id,
|
||||
Self::Trash(r#in) => r#in.id,
|
||||
Self::Link(r#in) => r#in.id,
|
||||
Self::Hardlink(r#in) => r#in.id,
|
||||
Self::Download(r#in) => r#in.id,
|
||||
Self::Upload(r#in) => r#in.id,
|
||||
Self::Preload(r#in) => r#in.id,
|
||||
Self::Copy(r#in) => r#in.id(),
|
||||
Self::Cut(r#in) => r#in.id(),
|
||||
Self::Delete(r#in) => r#in.id(),
|
||||
Self::Trash(r#in) => r#in.id(),
|
||||
Self::Link(r#in) => r#in.id(),
|
||||
Self::Hardlink(r#in) => r#in.id(),
|
||||
Self::Download(r#in) => r#in.id(),
|
||||
Self::Upload(r#in) => r#in.id(),
|
||||
Self::Preload(r#in) => r#in.id(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_id(self, id: Id) -> Self {
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
match self {
|
||||
Self::Copy(r#in) => Self::Copy(HookInOutCopy { id, ..r#in }),
|
||||
Self::Cut(r#in) => Self::Cut(HookInOutCut { id, ..r#in }),
|
||||
Self::Delete(r#in) => Self::Delete(HookInDelete { id, ..r#in }),
|
||||
Self::Trash(r#in) => Self::Trash(HookInTrash { id, ..r#in }),
|
||||
Self::Link(r#in) => Self::Link(HookInOutLink { id, ..r#in }),
|
||||
Self::Hardlink(r#in) => Self::Hardlink(HookInOutHardlink { id, ..r#in }),
|
||||
Self::Download(r#in) => Self::Download(HookInDownload { id, ..r#in }),
|
||||
Self::Upload(r#in) => Self::Upload(HookInUpload { id, ..r#in }),
|
||||
Self::Preload(r#in) => Self::Preload(HookInPreload { id, ..r#in }),
|
||||
Self::Copy(r#in) => r#in.id = id,
|
||||
Self::Cut(r#in) => r#in.id = id,
|
||||
Self::Delete(r#in) => r#in.id = id,
|
||||
Self::Trash(r#in) => r#in.id = id,
|
||||
Self::Link(r#in) => r#in.id = id,
|
||||
Self::Hardlink(r#in) => r#in.id = id,
|
||||
Self::Download(r#in) => r#in.id = id,
|
||||
Self::Upload(r#in) => r#in.id = id,
|
||||
Self::Preload(r#in) => r#in.id = id,
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
Self::Copy(r#in) => r#in.title(),
|
||||
Self::Cut(r#in) => r#in.title(),
|
||||
Self::Delete(r#in) => r#in.title(),
|
||||
Self::Trash(r#in) => r#in.title(),
|
||||
Self::Link(r#in) => r#in.title(),
|
||||
Self::Hardlink(r#in) => r#in.title(),
|
||||
Self::Download(r#in) => r#in.title(),
|
||||
Self::Upload(r#in) => r#in.title(),
|
||||
Self::Preload(r#in) => r#in.title(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +84,21 @@ pub(crate) struct HookInOutCopy {
|
|||
pub(crate) to: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInOutCopy {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Hook: copy {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl HookInOutCopy {
|
||||
pub(crate) fn new<U>(from: U, to: U) -> Self
|
||||
where
|
||||
|
|
@ -88,6 +122,21 @@ pub(crate) struct HookInOutCut {
|
|||
pub(crate) to: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInOutCut {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Hook: cut {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl HookInOutCut {
|
||||
pub(crate) fn new<U>(from: U, to: U) -> Self
|
||||
where
|
||||
|
|
@ -110,6 +159,19 @@ pub(crate) struct HookInDelete {
|
|||
pub(crate) target: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInDelete {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Hook: delete {}", self.target.display()).into() }
|
||||
}
|
||||
|
||||
impl HookInDelete {
|
||||
pub(crate) fn new<U>(target: U) -> Self
|
||||
where
|
||||
|
|
@ -126,6 +188,19 @@ pub(crate) struct HookInTrash {
|
|||
pub(crate) target: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInTrash {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Hook: trash {}", self.target.display()).into() }
|
||||
}
|
||||
|
||||
impl HookInTrash {
|
||||
pub(crate) fn new<U>(target: U) -> Self
|
||||
where
|
||||
|
|
@ -144,6 +219,21 @@ pub(crate) struct HookInOutLink {
|
|||
pub(crate) to: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInOutLink {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Hook: link {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl HookInOutLink {
|
||||
pub(crate) fn new<U>(from: U, to: U) -> Self
|
||||
where
|
||||
|
|
@ -168,6 +258,21 @@ pub(crate) struct HookInOutHardlink {
|
|||
pub(crate) to: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInOutHardlink {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
format!("Hook: hardlink {} to {}", self.from.display(), self.to.display()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl HookInOutHardlink {
|
||||
pub(crate) fn new<U>(from: U, to: U) -> Self
|
||||
where
|
||||
|
|
@ -190,6 +295,19 @@ pub(crate) struct HookInDownload {
|
|||
pub(crate) target: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInDownload {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Hook: download {}", self.target.display()).into() }
|
||||
}
|
||||
|
||||
impl HookInDownload {
|
||||
pub(crate) fn new<U>(target: U) -> Self
|
||||
where
|
||||
|
|
@ -206,6 +324,19 @@ pub(crate) struct HookInUpload {
|
|||
pub(crate) target: UrlBuf,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInUpload {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Hook: upload {}", self.target.display()).into() }
|
||||
}
|
||||
|
||||
impl HookInUpload {
|
||||
pub(crate) fn new<U>(target: U) -> Self
|
||||
where
|
||||
|
|
@ -223,6 +354,19 @@ pub(crate) struct HookInPreload {
|
|||
pub(crate) hash: u64,
|
||||
}
|
||||
|
||||
impl TaskIn for HookInPreload {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Hook: run {}-th preloader", self.idx).into() }
|
||||
}
|
||||
|
||||
impl HookInPreload {
|
||||
pub(crate) fn new(idx: u8, hash: u64) -> Self { Self { id: Id::ZERO, idx, hash } }
|
||||
}
|
||||
|
|
|
|||
13
yazi-scheduler/src/in.rs
Normal file
13
yazi-scheduler/src/in.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use yazi_shared::Id;
|
||||
|
||||
pub(crate) trait TaskIn {
|
||||
type Prog;
|
||||
|
||||
fn id(&self) -> Id;
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self;
|
||||
|
||||
fn title(&self) -> Cow<'_, str>;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ mod macros;
|
|||
|
||||
yazi_macro::mod_pub!(fetch file hook plugin preload process size);
|
||||
|
||||
yazi_macro::mod_flat!(behavior cleanup ongoing op out progress proxy scheduler snap summary task worker);
|
||||
yazi_macro::mod_flat!(behavior cleanup ongoing op out progress proxy r#in scheduler snap summary task worker);
|
||||
|
||||
const LOW: u8 = yazi_config::Priority::Low as u8;
|
||||
const NORMAL: u8 = yazi_config::Priority::Normal as u8;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use yazi_config::YAZI;
|
|||
use yazi_shared::{CompletionToken, Id, Ids};
|
||||
|
||||
use super::Task;
|
||||
use crate::{TaskProg, hook::HookIn};
|
||||
use crate::{TaskIn, TaskProg, hook::HookIn};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Ongoing {
|
||||
|
|
@ -11,11 +11,18 @@ pub struct Ongoing {
|
|||
}
|
||||
|
||||
impl Ongoing {
|
||||
pub(super) fn add(&mut self, name: String, prog: TaskProg) -> &mut Task {
|
||||
pub(super) fn add<T>(&mut self, r#in: &mut T) -> &mut Task
|
||||
where
|
||||
T: TaskIn,
|
||||
T::Prog: Into<TaskProg> + Default,
|
||||
{
|
||||
static IDS: Ids = Ids::new();
|
||||
|
||||
let id = IDS.next();
|
||||
self.inner.entry(id).insert(Task::new(id, name, prog)).into_mut()
|
||||
|
||||
let title = r#in.with_id(id).title().into_owned();
|
||||
let prog = T::Prog::default().into();
|
||||
|
||||
self.inner.entry(id).insert(Task::new(id, title, prog)).into_mut()
|
||||
}
|
||||
|
||||
pub(super) fn cancel(&mut self, id: Id) -> Option<HookIn> {
|
||||
|
|
|
|||
|
|
@ -1,33 +1,90 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use mlua::{ExternalError, FromLua, Lua, Value};
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_runner::entry::EntryJob;
|
||||
use yazi_shared::Id;
|
||||
use yazi_shared::{Id, SStr, data::{Data, DataKey}};
|
||||
|
||||
use crate::{TaskIn, plugin::PluginProgEntry};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PluginIn {
|
||||
Entry(PluginInEntry),
|
||||
}
|
||||
|
||||
impl_from_in!(Entry(PluginInEntry));
|
||||
impl TaskIn for PluginIn {
|
||||
type Prog = ();
|
||||
|
||||
impl PluginIn {
|
||||
pub(crate) fn id(&self) -> Id {
|
||||
fn id(&self) -> Id {
|
||||
match self {
|
||||
Self::Entry(r#in) => r#in.id,
|
||||
Self::Entry(r#in) => r#in.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
match self {
|
||||
Self::Entry(r#in) => _ = r#in.with_id(id),
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
Self::Entry(r#in) => r#in.title(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_in!(Entry(PluginInEntry));
|
||||
|
||||
// --- Entry
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PluginInEntry(pub(crate) EntryJob);
|
||||
|
||||
impl Deref for PluginInEntry {
|
||||
type Target = EntryJob;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PluginInEntry {
|
||||
pub id: Id,
|
||||
pub plugin: SStr,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
pub title: SStr,
|
||||
pub track: bool,
|
||||
}
|
||||
|
||||
impl DerefMut for PluginInEntry {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
|
||||
impl TaskIn for PluginInEntry {
|
||||
type Prog = PluginProgEntry;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
if self.title.is_empty() {
|
||||
format!("Run plugin '{}'", self.plugin).into()
|
||||
} else {
|
||||
Cow::Borrowed(&self.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginInEntry {
|
||||
pub(crate) fn into_job(self) -> EntryJob {
|
||||
EntryJob { id: self.id, args: self.args, plugin: self.plugin }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for PluginInEntry {
|
||||
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
|
||||
let Value::Table(t) = value else {
|
||||
return Err("constructing PluginInEntry from non-table value".into_lua_err());
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
id: Id::ZERO,
|
||||
plugin: t.raw_get::<String>(1)?.into(),
|
||||
args: Sendable::table_to_args(lua, t.raw_get("args")?)?,
|
||||
title: t.raw_get::<Option<String>>("title")?.unwrap_or_default().into(),
|
||||
track: t.raw_get("track")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ impl Plugin {
|
|||
pub(crate) async fn entry(&self, task: PluginInEntry) -> Result<(), PluginOutEntry> {
|
||||
let id = task.id;
|
||||
|
||||
RUNNER.entry(task.0).await?;
|
||||
RUNNER.entry(task.into_job()).await?;
|
||||
Ok(self.ops.out(id, PluginOutEntry::Succ))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,26 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use yazi_config::plugin::Preloader;
|
||||
use yazi_shared::Id;
|
||||
|
||||
use crate::{TaskIn, preload::PreloadProg};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct PreloadIn {
|
||||
pub(crate) id: Id,
|
||||
pub(crate) plugin: &'static Preloader,
|
||||
pub(crate) target: yazi_fs::File,
|
||||
}
|
||||
|
||||
impl TaskIn for PreloadIn {
|
||||
type Prog = PreloadProg;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Run preloader '{}'", self.plugin.run.name).into() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ impl Preload {
|
|||
self.loaded.lock().get_mut(&hash).map(|x| *x &= !(1 << task.plugin.idx));
|
||||
}
|
||||
if let Some(e) = state.error {
|
||||
error!("Error when running preloader `{}`:\n{e}", task.plugin.run.name);
|
||||
error!("Error when running preloader '{}':\n{e}", task.plugin.run.name);
|
||||
}
|
||||
|
||||
Ok(self.ops.out(task.id, PreloadOut::Succ))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use std::ffi::OsString;
|
||||
use std::{borrow::Cow, ffi::OsString};
|
||||
|
||||
use yazi_shared::{CompletionToken, Id, url::UrlCow};
|
||||
|
||||
use super::ShellOpt;
|
||||
use crate::{TaskIn, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ProcessIn {
|
||||
|
|
@ -13,14 +14,33 @@ pub(crate) enum ProcessIn {
|
|||
|
||||
impl_from_in!(Block(ProcessInBlock), Orphan(ProcessInOrphan), Bg(ProcessInBg));
|
||||
|
||||
impl ProcessIn {
|
||||
pub(crate) fn id(&self) -> Id {
|
||||
impl TaskIn for ProcessIn {
|
||||
type Prog = ();
|
||||
|
||||
fn id(&self) -> Id {
|
||||
match self {
|
||||
Self::Block(r#in) => r#in.id,
|
||||
Self::Orphan(r#in) => r#in.id,
|
||||
Self::Bg(r#in) => r#in.id,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
match self {
|
||||
Self::Block(r#in) => _ = r#in.with_id(id),
|
||||
Self::Orphan(r#in) => _ = r#in.with_id(id),
|
||||
Self::Bg(r#in) => _ = r#in.with_id(id),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
Self::Block(r#in) => r#in.title(),
|
||||
Self::Orphan(r#in) => r#in.title(),
|
||||
Self::Bg(r#in) => r#in.title(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Block
|
||||
|
|
@ -32,6 +52,19 @@ pub(crate) struct ProcessInBlock {
|
|||
pub(crate) args: Vec<UrlCow<'static>>,
|
||||
}
|
||||
|
||||
impl TaskIn for ProcessInBlock {
|
||||
type Prog = ProcessProgBlock;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Blocking command: {}", self.cmd.display()).into() }
|
||||
}
|
||||
|
||||
impl From<ProcessInBlock> for ShellOpt {
|
||||
fn from(r#in: ProcessInBlock) -> Self {
|
||||
Self { cwd: r#in.cwd, cmd: r#in.cmd, args: r#in.args, piped: false, orphan: false }
|
||||
|
|
@ -47,6 +80,19 @@ pub(crate) struct ProcessInOrphan {
|
|||
pub(crate) args: Vec<UrlCow<'static>>,
|
||||
}
|
||||
|
||||
impl TaskIn for ProcessInOrphan {
|
||||
type Prog = ProcessProgOrphan;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Orphan command: {}", self.cmd.display()).into() }
|
||||
}
|
||||
|
||||
impl From<ProcessInOrphan> for ShellOpt {
|
||||
fn from(r#in: ProcessInOrphan) -> Self {
|
||||
Self { cwd: r#in.cwd, cmd: r#in.cmd, args: r#in.args, piped: false, orphan: true }
|
||||
|
|
@ -63,6 +109,19 @@ pub(crate) struct ProcessInBg {
|
|||
pub(crate) done: CompletionToken,
|
||||
}
|
||||
|
||||
impl TaskIn for ProcessInBg {
|
||||
type Prog = ProcessProgBg;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Background command: {}", self.cmd.display()).into() }
|
||||
}
|
||||
|
||||
impl From<ProcessInBg> for ShellOpt {
|
||||
fn from(r#in: ProcessInBg) -> Self {
|
||||
Self { cwd: r#in.cwd, cmd: r#in.cmd, args: r#in.args, piped: true, orphan: false }
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
use std::{ops::Deref, sync::Arc, time::Duration};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use tokio::task::JoinHandle;
|
||||
use yazi_config::{YAZI, plugin::{Fetcher, Preloader}};
|
||||
use yazi_fs::FsHash64;
|
||||
use yazi_runner::entry::EntryJob;
|
||||
use yazi_shared::{CompletionToken, Id, SStr, Throttle, data::{Data, DataKey}, url::{UrlBuf, UrlLike}};
|
||||
use yazi_shared::{CompletionToken, Id, Throttle, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use crate::{Behavior, HIGH, LOW, NORMAL, Task, TaskProg, Worker, fetch::{FetchIn, FetchProg}, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload, FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInPreload, HookInTrash, HookInUpload}, plugin::{PluginInEntry, PluginProgEntry}, preload::{PreloadIn, PreloadProg}, process::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessOpt, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::{SizeIn, SizeProg}};
|
||||
use crate::{Behavior, HIGH, LOW, NORMAL, Task, TaskIn, TaskProg, Worker, fetch::FetchIn, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInPreload, HookInTrash, HookInUpload}, plugin::PluginInEntry, preload::PreloadIn, process::{ProcessIn, ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessOpt}, size::SizeIn};
|
||||
|
||||
pub struct Scheduler {
|
||||
pub worker: Worker,
|
||||
|
|
@ -27,14 +25,13 @@ impl Scheduler {
|
|||
Self { worker, behavior: Behavior::new(), handles }
|
||||
}
|
||||
|
||||
fn add<T, R>(&self, name: String, map: impl FnOnce(&mut Task) -> R) -> R
|
||||
fn add<T, R>(&self, r#in: &mut T, map: impl FnOnce(&mut Task) -> R) -> R
|
||||
where
|
||||
T: Into<TaskProg> + Default,
|
||||
T: TaskIn,
|
||||
T::Prog: Into<TaskProg> + Default,
|
||||
{
|
||||
let prog = T::default().into();
|
||||
|
||||
let mut ongoing = self.ongoing.lock();
|
||||
let task = ongoing.add(name, prog);
|
||||
let task = ongoing.add(r#in);
|
||||
|
||||
self.behavior.update(task.id);
|
||||
map(task)
|
||||
|
|
@ -42,14 +39,15 @@ impl Scheduler {
|
|||
|
||||
fn add_hooked<T, R>(
|
||||
&self,
|
||||
name: String,
|
||||
r#in: &mut T,
|
||||
hook: impl Into<HookIn>,
|
||||
map: impl FnOnce(&mut Task) -> R,
|
||||
) -> R
|
||||
where
|
||||
T: Into<TaskProg> + Default,
|
||||
T: TaskIn,
|
||||
T::Prog: Into<TaskProg> + Default,
|
||||
{
|
||||
self.add::<T, R>(name, |t| map(t.with_hook(hook)))
|
||||
self.add(r#in, |t| map(t.with_hook(hook)))
|
||||
}
|
||||
|
||||
pub fn cancel(&self, id: Id) -> bool {
|
||||
|
|
@ -68,111 +66,115 @@ impl Scheduler {
|
|||
}
|
||||
|
||||
pub fn file_cut(&self, from: UrlBuf, to: UrlBuf, force: bool) {
|
||||
let name = format!("Cut {} to {}", from.display(), to.display());
|
||||
let id = self.add::<FileProgCut, _>(name, |t| t.id);
|
||||
|
||||
if to.try_starts_with(&from).unwrap_or(false) && !to.covariant(&from) {
|
||||
return self.ops.out(id, FileOutCut::Fail("Cannot cut directory into itself".to_owned()));
|
||||
}
|
||||
|
||||
let follow = !from.scheme().covariant(to.scheme());
|
||||
self
|
||||
.file
|
||||
.submit(FileInCut { id, from, to, force, cha: None, follow, retry: 0, drop: None }, LOW);
|
||||
let mut r#in =
|
||||
FileInCut { id: Id::ZERO, from, to, force, cha: None, follow, retry: 0, drop: None };
|
||||
|
||||
self.add(&mut r#in, |_| ());
|
||||
if r#in.to.try_starts_with(&r#in.from).unwrap_or(false) && !r#in.to.covariant(&r#in.from) {
|
||||
self.ops.out(r#in.id, FileOutCut::Fail("Cannot cut directory into itself".to_owned()));
|
||||
} else {
|
||||
self.file.submit(r#in, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_copy(&self, from: UrlBuf, to: UrlBuf, force: bool, follow: bool) {
|
||||
let name = format!("Copy {} to {}", from.display(), to.display());
|
||||
let id = self.add::<FileProgCopy, _>(name, |t| t.id);
|
||||
|
||||
if to.try_starts_with(&from).unwrap_or(false) && !to.covariant(&from) {
|
||||
return self.ops.out(id, FileOutCopy::Fail("Cannot copy directory into itself".to_owned()));
|
||||
}
|
||||
|
||||
let follow = follow || !from.scheme().covariant(to.scheme());
|
||||
self.file.submit(FileInCopy { id, from, to, force, cha: None, follow, retry: 0 }, LOW);
|
||||
let mut r#in = FileInCopy { id: Id::ZERO, from, to, force, cha: None, follow, retry: 0 };
|
||||
|
||||
self.add(&mut r#in, |_| ());
|
||||
if r#in.to.try_starts_with(&r#in.from).unwrap_or(false) && !r#in.to.covariant(&r#in.from) {
|
||||
self.ops.out(r#in.id, FileOutCopy::Fail("Cannot copy directory into itself".to_owned()));
|
||||
} else {
|
||||
self.file.submit(r#in, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_link(&self, from: UrlBuf, to: UrlBuf, relative: bool, force: bool) {
|
||||
let name = format!("Link {} to {}", from.display(), to.display());
|
||||
let id = self.add::<FileProgLink, _>(name, |t| t.id);
|
||||
let mut r#in = FileInLink {
|
||||
id: Id::ZERO,
|
||||
from,
|
||||
to,
|
||||
force,
|
||||
cha: None,
|
||||
resolve: false,
|
||||
relative,
|
||||
delete: false,
|
||||
};
|
||||
|
||||
self.file.submit(
|
||||
FileInLink { id, from, to, force, cha: None, resolve: false, relative, delete: false },
|
||||
LOW,
|
||||
);
|
||||
self.add(&mut r#in, |_| ());
|
||||
self.file.submit(r#in, LOW);
|
||||
}
|
||||
|
||||
pub fn file_hardlink(&self, from: UrlBuf, to: UrlBuf, force: bool, follow: bool) {
|
||||
let name = format!("Hardlink {} to {}", from.display(), to.display());
|
||||
let id = self.add::<FileProgHardlink, _>(name, |t| t.id);
|
||||
let mut r#in = FileInHardlink { id: Id::ZERO, from, to, force, cha: None, follow };
|
||||
self.add(&mut r#in, |_| ());
|
||||
|
||||
if !from.scheme().covariant(to.scheme()) {
|
||||
if !r#in.from.scheme().covariant(r#in.to.scheme()) {
|
||||
return self
|
||||
.ops
|
||||
.out(id, FileOutHardlink::Fail("Cannot hardlink cross filesystem".to_owned()));
|
||||
.out(r#in.id, FileOutHardlink::Fail("Cannot hardlink cross filesystem".to_owned()));
|
||||
}
|
||||
|
||||
if to.try_starts_with(&from).unwrap_or(false) && !to.covariant(&from) {
|
||||
if r#in.to.try_starts_with(&r#in.from).unwrap_or(false) && !r#in.to.covariant(&r#in.from) {
|
||||
return self
|
||||
.ops
|
||||
.out(id, FileOutHardlink::Fail("Cannot hardlink directory into itself".to_owned()));
|
||||
.out(r#in.id, FileOutHardlink::Fail("Cannot hardlink directory into itself".to_owned()));
|
||||
}
|
||||
|
||||
self.file.submit(FileInHardlink { id, from, to, force, cha: None, follow }, LOW);
|
||||
self.file.submit(r#in, LOW);
|
||||
}
|
||||
|
||||
pub fn file_delete(&self, target: UrlBuf) {
|
||||
let name = format!("Delete {}", target.display());
|
||||
let mut r#in = FileInDelete { id: Id::ZERO, target, cha: None };
|
||||
let hook = HookInDelete::new(&r#in.target);
|
||||
|
||||
let hook = HookInDelete::new(&target);
|
||||
let id = self.add_hooked::<FileProgDelete, _>(name, hook, |t| t.id);
|
||||
|
||||
self.file.submit(FileInDelete { id, target, cha: None }, LOW);
|
||||
self.add_hooked(&mut r#in, hook, |_| ());
|
||||
self.file.submit(r#in, LOW);
|
||||
}
|
||||
|
||||
pub fn file_trash(&self, target: UrlBuf) {
|
||||
let name = format!("Trash {}", target.display());
|
||||
let mut r#in = FileInTrash { id: Id::ZERO, target };
|
||||
let hook = HookInTrash::new(&r#in.target);
|
||||
|
||||
let hook = HookInTrash::new(&target);
|
||||
let id = self.add_hooked::<FileProgTrash, _>(name, hook, |t| t.id);
|
||||
|
||||
self.file.submit(FileInTrash { id, target }, LOW);
|
||||
self.add_hooked(&mut r#in, hook, |_| ());
|
||||
self.file.submit(r#in, LOW);
|
||||
}
|
||||
|
||||
pub fn file_download(&self, target: UrlBuf) -> CompletionToken {
|
||||
let name = format!("Download {}", target.display());
|
||||
let mut r#in = FileInDownload { id: Id::ZERO, target, cha: None, retry: 0 };
|
||||
let hook = HookInDownload::new(&r#in.target);
|
||||
let done = self.add_hooked(&mut r#in, hook, |t| t.done.clone());
|
||||
|
||||
let hook = HookInDownload::new(&target);
|
||||
let (id, done) = self.add_hooked::<FileProgDownload, _>(name, hook, |t| (t.id, t.done.clone()));
|
||||
|
||||
if !target.kind().is_remote() {
|
||||
self.ops.out(id, FileOutDownload::Fail("Cannot download non-remote file".to_owned()));
|
||||
return done;
|
||||
if r#in.target.kind().is_remote() {
|
||||
self.file.submit(r#in, LOW);
|
||||
} else {
|
||||
self.ops.out(r#in.id, FileOutDownload::Fail("Cannot download non-remote file".to_owned()));
|
||||
}
|
||||
|
||||
self.file.submit(FileInDownload { id, target, cha: None, retry: 0 }, LOW);
|
||||
done
|
||||
}
|
||||
|
||||
pub fn file_upload(&self, target: UrlBuf) {
|
||||
let name = format!("Upload {}", target.display());
|
||||
let mut r#in = FileInUpload { id: Id::ZERO, target, cha: None, cache: None };
|
||||
let hook = HookInUpload::new(&r#in.target);
|
||||
self.add_hooked(&mut r#in, hook, |_| ());
|
||||
|
||||
let hook = HookInUpload::new(&target);
|
||||
let id = self.add_hooked::<FileProgUpload, _>(name, hook, |t| t.id);
|
||||
|
||||
if !target.kind().is_remote() {
|
||||
return self.ops.out(id, FileOutUpload::Fail("Cannot upload non-remote file".to_owned()));
|
||||
if r#in.target.kind().is_remote() {
|
||||
self.file.submit(r#in, LOW);
|
||||
} else {
|
||||
self.ops.out(r#in.id, FileOutUpload::Fail("Cannot upload non-remote file".to_owned()));
|
||||
}
|
||||
|
||||
self.file.submit(FileInUpload { id, target, cha: None, cache: None }, LOW);
|
||||
}
|
||||
|
||||
pub fn plugin_entry(&self, plugin: SStr, args: HashMap<DataKey, Data>) {
|
||||
let name = format!("Run micro plugin `{plugin}`");
|
||||
let id = self.add::<PluginProgEntry, _>(name, |t| t.id);
|
||||
pub fn plugin_entry(&self, mut r#in: PluginInEntry) -> Id {
|
||||
if r#in.track {
|
||||
self.behavior.reset();
|
||||
}
|
||||
|
||||
self.plugin.submit(PluginInEntry(EntryJob { id, args, plugin }), NORMAL);
|
||||
let id = self.add(&mut r#in, |t| t.id);
|
||||
self.plugin.submit(r#in, NORMAL);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn fetch_paged(
|
||||
|
|
@ -180,10 +182,11 @@ impl Scheduler {
|
|||
fetcher: &'static Fetcher,
|
||||
targets: Vec<yazi_fs::File>,
|
||||
) -> CompletionToken {
|
||||
let name = format!("Run fetcher `{}` with {} target(s)", fetcher.run.name, targets.len());
|
||||
let (id, done) = self.add::<FetchProg, _>(name, |t| (t.id, t.done.clone()));
|
||||
let mut r#in = FetchIn { id: Id::ZERO, plugin: fetcher, targets };
|
||||
|
||||
let done = self.add(&mut r#in, |t| t.done.clone());
|
||||
self.fetch.submit(r#in);
|
||||
|
||||
self.fetch.submit(FetchIn { id, plugin: fetcher, targets });
|
||||
done
|
||||
}
|
||||
|
||||
|
|
@ -202,61 +205,55 @@ impl Scheduler {
|
|||
}
|
||||
|
||||
pub fn preload_paged(&self, preloader: &'static Preloader, target: &yazi_fs::File) {
|
||||
let name = format!("Run preloader `{}`", preloader.run.name);
|
||||
let mut r#in = PreloadIn { id: Id::ZERO, plugin: preloader, target: target.clone() };
|
||||
let hook = HookInPreload::new(preloader.idx, target.hash_u64());
|
||||
|
||||
let id = self.add_hooked::<PreloadProg, _>(name, hook, |t| t.id);
|
||||
if let Some(prev) = self.preload.loading.lock().put(target.url.hash_u64(), id) {
|
||||
self.add_hooked(&mut r#in, hook, |_| ());
|
||||
if let Some(prev) = self.preload.loading.lock().put(target.url.hash_u64(), r#in.id) {
|
||||
self.cancel(prev);
|
||||
}
|
||||
|
||||
self.preload.submit(PreloadIn { id, plugin: preloader, target: target.clone() });
|
||||
self.preload.submit(r#in);
|
||||
}
|
||||
|
||||
pub fn prework_size(&self, targets: Vec<&UrlBuf>) {
|
||||
let throttle = Arc::new(Throttle::new(targets.len(), Duration::from_millis(300)));
|
||||
|
||||
for target in targets {
|
||||
let name = format!("Calculate the size of {}", target.display());
|
||||
let id = self.add::<SizeProg, _>(name, |t| t.id);
|
||||
let mut r#in =
|
||||
SizeIn { id: Id::ZERO, target: target.clone(), throttle: throttle.clone() };
|
||||
|
||||
self.size.submit(SizeIn { id, target: target.clone(), throttle: throttle.clone() }, NORMAL);
|
||||
self.add(&mut r#in, |_| ());
|
||||
self.size.submit(r#in, NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_open(&self, opt: ProcessOpt) -> CompletionToken {
|
||||
let name = {
|
||||
let args = opt.args.iter().map(|a| a.display().to_string()).collect::<Vec<_>>().join(" ");
|
||||
if args.is_empty() {
|
||||
format!("Run {:?}", opt.cmd)
|
||||
} else {
|
||||
format!("Run {:?} with `{args}`", opt.cmd)
|
||||
let mut r#in: ProcessIn = if opt.block {
|
||||
ProcessInBlock { id: Id::ZERO, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }.into()
|
||||
} else if opt.orphan {
|
||||
ProcessInOrphan { id: Id::ZERO, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }.into()
|
||||
} else {
|
||||
ProcessInBg {
|
||||
id: Id::ZERO,
|
||||
cwd: opt.cwd,
|
||||
cmd: opt.cmd,
|
||||
args: opt.args,
|
||||
done: CompletionToken::default(),
|
||||
}
|
||||
.into()
|
||||
};
|
||||
|
||||
let done = match &mut r#in {
|
||||
ProcessIn::Block(r#in) => self.add(r#in, |t| t.done.clone()),
|
||||
ProcessIn::Orphan(r#in) => self.add(r#in, |t| t.done.clone()),
|
||||
ProcessIn::Bg(r#in) => {
|
||||
r#in.done = self.add(r#in, |t| t.done.clone());
|
||||
r#in.done.clone()
|
||||
}
|
||||
};
|
||||
|
||||
let (id, done) = if opt.block {
|
||||
self.add::<ProcessProgBlock, _>(name, |t| (t.id, t.done.clone()))
|
||||
} else if opt.orphan {
|
||||
self.add::<ProcessProgOrphan, _>(name, |t| (t.id, t.done.clone()))
|
||||
} else {
|
||||
self.add::<ProcessProgBg, _>(name, |t| (t.id, t.done.clone()))
|
||||
};
|
||||
|
||||
if opt.block {
|
||||
self
|
||||
.process
|
||||
.submit(ProcessInBlock { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }, NORMAL);
|
||||
} else if opt.orphan {
|
||||
self
|
||||
.process
|
||||
.submit(ProcessInOrphan { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }, NORMAL);
|
||||
} else {
|
||||
self.process.submit(
|
||||
ProcessInBg { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args, done: done.clone() },
|
||||
NORMAL,
|
||||
);
|
||||
};
|
||||
|
||||
self.process.submit(r#in, NORMAL);
|
||||
done
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use yazi_shared::{Id, Throttle, url::UrlBuf};
|
||||
use yazi_shared::{Id, Throttle, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use crate::{TaskIn, size::SizeProg};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SizeIn {
|
||||
|
|
@ -9,6 +11,15 @@ pub(crate) struct SizeIn {
|
|||
pub(crate) throttle: Arc<Throttle<(UrlBuf, u64)>>,
|
||||
}
|
||||
|
||||
impl SizeIn {
|
||||
pub(crate) fn id(&self) -> Id { self.id }
|
||||
impl TaskIn for SizeIn {
|
||||
type Prog = SizeProg;
|
||||
|
||||
fn id(&self) -> Id { self.id }
|
||||
|
||||
fn with_id(&mut self, id: Id) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(&self) -> Cow<'_, str> { format!("Size '{}'", self.target.display()).into() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ use crate::{Task, TaskProg};
|
|||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
pub struct TaskSnap {
|
||||
pub name: String,
|
||||
pub prog: TaskProg,
|
||||
pub title: String,
|
||||
pub prog: TaskProg,
|
||||
}
|
||||
|
||||
impl From<&Task> for TaskSnap {
|
||||
fn from(task: &Task) -> Self { Self { name: task.name.clone(), prog: task.prog } }
|
||||
fn from(task: &Task) -> Self { Self { title: task.title.clone(), prog: task.prog } }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use tokio::sync::mpsc;
|
||||
use yazi_shared::{CompletionToken, Id};
|
||||
|
||||
use crate::{TaskProg, hook::HookIn};
|
||||
use crate::{TaskIn, TaskProg, hook::HookIn};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Task {
|
||||
pub id: Id,
|
||||
pub name: String,
|
||||
pub title: String,
|
||||
pub(crate) prog: TaskProg,
|
||||
pub(crate) hook: Option<HookIn>,
|
||||
pub done: CompletionToken,
|
||||
|
|
@ -16,10 +16,10 @@ pub struct Task {
|
|||
}
|
||||
|
||||
impl Task {
|
||||
pub(super) fn new(id: Id, name: String, prog: TaskProg) -> Self {
|
||||
pub(super) fn new(id: Id, title: String, prog: TaskProg) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
title,
|
||||
prog,
|
||||
hook: None,
|
||||
done: Default::default(),
|
||||
|
|
@ -39,7 +39,10 @@ impl Task {
|
|||
}
|
||||
|
||||
pub(super) fn with_hook(&mut self, hook: impl Into<HookIn>) -> &mut Self {
|
||||
self.hook = Some(hook.into().with_id(self.id));
|
||||
let mut hook = hook.into();
|
||||
hook.with_id(self.id);
|
||||
|
||||
self.hook = Some(hook);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use parking_lot::Mutex;
|
|||
use tokio::{select, sync::mpsc, task::JoinHandle};
|
||||
use yazi_config::YAZI;
|
||||
|
||||
use crate::{CleanupState, LOW, Ongoing, Progress, TaskOp, TaskOps, TaskOut, fetch::{Fetch, FetchIn}, file::{File, FileIn}, hook::{Hook, HookIn}, plugin::{Plugin, PluginIn}, preload::{Preload, PreloadIn}, process::{Process, ProcessIn}, size::{Size, SizeIn}};
|
||||
use crate::{CleanupState, LOW, Ongoing, Progress, TaskIn, TaskOp, TaskOps, TaskOut, fetch::{Fetch, FetchIn}, file::{File, FileIn}, hook::{Hook, HookIn}, plugin::{Plugin, PluginIn}, preload::{Preload, PreloadIn}, process::{Process, ProcessIn}, size::{Size, SizeIn}};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Worker {
|
||||
|
|
|
|||
|
|
@ -17,19 +17,32 @@ where
|
|||
Ok(integer)
|
||||
}
|
||||
|
||||
macro_rules! ref_or_owned {
|
||||
(ref, $value:expr) => {
|
||||
&$value
|
||||
};
|
||||
(owned, $value:expr) => {
|
||||
$value
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_into_integer {
|
||||
($a:ty, $($b:ty),+ $(,)?) => {
|
||||
impl_into_integer!(@impl ref $a, $a, $($b),+);
|
||||
impl_into_integer!(@impl owned $a, &$a, $($b),+);
|
||||
};
|
||||
(@impl $kind:ident $t:ty, $a:ty, $($b:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl TryFrom<&$a> for $b {
|
||||
impl TryFrom<$a> for $b {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &$a) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: $a) -> Result<Self, Self::Error> {
|
||||
paste::paste! {
|
||||
Ok(match value {
|
||||
$a::Integer(i) => <$b>::try_from(*i)?,
|
||||
$a::Number(n) => <$b>::try_from($crate::data::macros::float_to_i64(*n)?)?,
|
||||
$a::String(s) => s.parse()?,
|
||||
$a::Id(i) => <$b>::try_from(i.get())?,
|
||||
Ok(match ref_or_owned!($kind, value) {
|
||||
$t::Integer(i) => <$b>::try_from(*i)?,
|
||||
$t::Number(n) => <$b>::try_from($crate::data::macros::float_to_i64(*n)?)?,
|
||||
$t::String(s) => s.parse()?,
|
||||
$t::Id(i) => <$b>::try_from(i.get())?,
|
||||
_ => anyhow::bail!("not an integer"),
|
||||
})
|
||||
}
|
||||
|
|
@ -41,17 +54,21 @@ macro_rules! impl_into_integer {
|
|||
|
||||
macro_rules! impl_into_number {
|
||||
($a:ty, $($b:ty),+ $(,)?) => {
|
||||
impl_into_number!(@impl ref $a, $a, $($b),+);
|
||||
impl_into_number!(@impl owned $a, &$a, $($b),+);
|
||||
};
|
||||
(@impl $kind:ident $t:ty, $a:ty, $($b:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl TryFrom<&$a> for $b {
|
||||
impl TryFrom<$a> for $b {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &$a) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: $a) -> Result<Self, Self::Error> {
|
||||
paste::paste! {
|
||||
Ok(match value {
|
||||
$a::Integer(i) if *i == (*i as $b as _) => *i as $b,
|
||||
$a::Number(n) if f64::from(*n) == (f64::from(*n) as $b as _) => f64::from(*n) as $b,
|
||||
$a::String(s) => s.parse()?,
|
||||
$a::Id(i) if i.get() == (i.get() as $b as _) => i.get() as $b,
|
||||
Ok(match ref_or_owned!($kind, value) {
|
||||
$t::Integer(i) if *i == (*i as $b as _) => *i as $b,
|
||||
$t::Number(n) if f64::from(*n) == (f64::from(*n) as $b as _) => f64::from(*n) as $b,
|
||||
$t::String(s) => s.parse()?,
|
||||
$t::Id(i) if i.get() == (i.get() as $b as _) => i.get() as $b,
|
||||
_ => anyhow::bail!("not a number"),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ futures = { workspace = true }
|
|||
mlua = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
ratatui = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
unicode-width = { workspace = true }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{num::ParseIntError, str::FromStr};
|
||||
use std::{fmt, num::ParseIntError, str::FromStr};
|
||||
|
||||
use yazi_shared::data::Data;
|
||||
use serde::{Deserialize, Deserializer, de};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Step {
|
||||
|
|
@ -35,15 +35,57 @@ impl FromStr for Step {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Data> for Step {
|
||||
type Error = ParseIntError;
|
||||
impl<'de> Deserialize<'de> for Step {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct Visitor;
|
||||
|
||||
fn try_from(value: &Data) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
Data::Integer(i) => Self::from(*i as isize),
|
||||
Data::String(s) => s.parse()?,
|
||||
_ => "".parse()?,
|
||||
})
|
||||
impl de::Visitor<'_> for Visitor {
|
||||
type Value = Step;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a step string or integer offset")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
value.parse().map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_borrowed_str<E>(self, value: &'_ str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
self.visit_str(value)
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
self.visit_str(&value)
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
isize::try_from(value).map(Self::Value::from).map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
isize::try_from(value).map(Self::Value::from).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue