refactor: unify progress representation and add cleanup state

This commit is contained in:
sxyazi 2026-04-05 13:39:26 +08:00
parent 4bb4f37555
commit 187063e3a2
No known key found for this signature in database
17 changed files with 234 additions and 387 deletions

1
Cargo.lock generated
View file

@ -6058,6 +6058,7 @@ dependencies = [
"ordered-float 5.3.0",
"parking_lot",
"serde",
"strum 0.28.0",
"tokio",
"tokio-util",
"tracing",

View file

@ -2,6 +2,7 @@ use std::ops::Deref;
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value};
use yazi_binding::{SER_OPT, cached_field};
use yazi_scheduler::Progress;
use super::{Lives, PtrCell};
@ -29,8 +30,8 @@ impl UserData for TaskSnap {
cached_field!(fields, name, |lua, me| lua.create_string(&me.name));
cached_field!(fields, prog, |lua, me| lua.to_value_with(&me.prog, SER_OPT));
fields.add_field_method_get("cooked", |_, me| Ok(me.prog.cooked()));
fields.add_field_method_get("running", |_, me| Ok(me.prog.running()));
fields.add_field_method_get("cooked", |_, me| Ok(me.prog.cooked()));
fields.add_field_method_get("success", |_, me| Ok(me.prog.success()));
fields.add_field_method_get("failed", |_, me| Ok(me.prog.failed()));
fields.add_field_method_get("percent", |_, me| Ok(me.prog.percent()));

View file

@ -33,6 +33,7 @@ mlua = { workspace = true }
ordered-float = { workspace = true }
parking_lot = { workspace = true }
serde = { workspace = true }
strum = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true }
tracing = { workspace = true }

View file

@ -0,0 +1,11 @@
use serde::Serialize;
use strum::EnumIs;
#[derive(Clone, Copy, Debug, Default, EnumIs, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum CleanupState {
#[default]
Pending,
Success,
Failed,
}

View file

@ -1,6 +1,6 @@
use serde::Serialize;
use crate::TaskSummary;
use crate::{Progress, TaskSummary};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FetchProg {
@ -18,16 +18,10 @@ impl From<FetchProg> for TaskSummary {
}
}
impl FetchProg {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for FetchProg {
fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cooked() }
pub fn failed(self) -> bool { self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { None }
pub fn percent(self) -> Option<f32> { None }
fn failed(self) -> bool { self.state == Some(false) }
}

View file

@ -1,6 +1,6 @@
use std::io;
use crate::{Task, TaskProg};
use crate::{CleanupState, Task, TaskProg};
// --- Copy
#[derive(Debug)]
@ -41,7 +41,7 @@ impl FileOutCopy {
task.log(reason);
}
Self::Clean => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
}
}
@ -120,10 +120,10 @@ impl FileOutCut {
task.log(reason);
}
Self::Clean(Ok(())) => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
Self::Clean(Err(reason)) => {
prog.cleaned = Some(false);
prog.cleaned = CleanupState::Failed;
task.log(format!("Failed cleaning up cut file: {reason:?}"));
}
}
@ -188,7 +188,7 @@ impl FileOutLink {
task.log(reason);
}
Self::Clean => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
}
} else if let TaskProg::FileCopy(prog) = &mut task.prog {
@ -255,7 +255,7 @@ impl FileOutHardlink {
task.log(reason);
}
Self::Clean => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
}
}
@ -316,10 +316,10 @@ impl FileOutDelete {
task.log(reason);
}
Self::Clean(Ok(())) => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
Self::Clean(Err(reason)) => {
prog.cleaned = Some(false);
prog.cleaned = CleanupState::Failed;
task.log(format!("Failed cleaning up deleted file: {reason:?}"));
}
}
@ -377,7 +377,7 @@ impl FileOutTrash {
task.log(reason);
}
Self::Clean => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
}
}
@ -418,7 +418,7 @@ impl FileOutDownload {
task.log(reason);
}
Self::Clean => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
}
}
@ -493,7 +493,7 @@ impl FileOutUpload {
task.log(reason);
}
Self::Clean => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
}
}

View file

@ -1,6 +1,6 @@
use serde::Serialize;
use crate::TaskSummary;
use crate::{CleanupState, Progress, TaskSummary};
// --- Copy
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
@ -11,7 +11,7 @@ pub struct FileProgCopy {
pub total_bytes: u64,
pub processed_bytes: u64,
pub collected: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgCopy> for TaskSummary {
@ -25,33 +25,21 @@ impl From<FileProgCopy> for TaskSummary {
}
}
impl FileProgCopy {
pub fn cooked(self) -> bool {
self.collected == Some(true) && self.success_files == self.total_files
impl Progress for FileProgCopy {
fn running(self) -> bool {
self.cooking_or_cleaning(
self.collected.is_none() || self.success_files + self.failed_files != self.total_files,
)
}
pub fn running(self) -> bool {
self.collected.is_none()
|| self.success_files + self.failed_files != self.total_files
|| (self.cleaned.is_none() && self.cooked())
}
fn cooked(self) -> bool { self.collected == Some(true) && self.success_files == self.total_files }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.collected == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> {
Some(if self.success() {
100.0
} else if self.failed() {
0.0
} else if self.total_bytes != 0 {
99.99f32.min(self.processed_bytes as f32 / self.total_bytes as f32 * 100.0)
} else {
99.99
})
fn percent(self) -> Option<f32> {
Some(self.byte_percent(self.processed_bytes, self.total_bytes))
}
}
@ -64,7 +52,7 @@ pub struct FileProgCut {
pub total_bytes: u64,
pub processed_bytes: u64,
pub collected: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgCut> for TaskSummary {
@ -78,33 +66,21 @@ impl From<FileProgCut> for TaskSummary {
}
}
impl FileProgCut {
pub fn cooked(self) -> bool {
self.collected == Some(true) && self.success_files == self.total_files
impl Progress for FileProgCut {
fn running(self) -> bool {
self.cooking_or_cleaning(
self.collected.is_none() || self.success_files + self.failed_files != self.total_files,
)
}
pub fn running(self) -> bool {
self.collected.is_none()
|| self.success_files + self.failed_files != self.total_files
|| (self.cleaned.is_none() && self.cooked())
}
fn cooked(self) -> bool { self.collected == Some(true) && self.success_files == self.total_files }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.collected == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> {
Some(if self.success() {
100.0
} else if self.failed() {
0.0
} else if self.total_bytes != 0 {
99.99f32.min(self.processed_bytes as f32 / self.total_bytes as f32 * 100.0)
} else {
99.99
})
fn percent(self) -> Option<f32> {
Some(self.byte_percent(self.processed_bytes, self.total_bytes))
}
}
@ -112,7 +88,7 @@ impl FileProgCut {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FileProgLink {
pub state: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgLink> for TaskSummary {
@ -126,18 +102,14 @@ impl From<FileProgLink> for TaskSummary {
}
}
impl FileProgLink {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for FileProgLink {
fn running(self) -> bool { self.cooking_or_cleaning(self.state.is_none()) }
pub fn running(self) -> bool { self.state.is_none() || (self.cleaned.is_none() && self.cooked()) }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.state == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> { None }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
}
// --- Hardlink
@ -147,7 +119,7 @@ pub struct FileProgHardlink {
pub success: u32,
pub failed: u32,
pub collected: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgHardlink> for TaskSummary {
@ -161,22 +133,16 @@ impl From<FileProgHardlink> for TaskSummary {
}
}
impl FileProgHardlink {
pub fn cooked(self) -> bool { self.collected == Some(true) && self.success == self.total }
pub fn running(self) -> bool {
self.collected.is_none()
|| self.success + self.failed != self.total
|| (self.cleaned.is_none() && self.cooked())
impl Progress for FileProgHardlink {
fn running(self) -> bool {
self.cooking_or_cleaning(self.collected.is_none() || self.success + self.failed != self.total)
}
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn cooked(self) -> bool { self.collected == Some(true) && self.success == self.total }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) }
fn failed(self) -> bool { self.cleaned.is_failed() || self.collected == Some(false) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> { None }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
}
// --- Delete
@ -188,7 +154,7 @@ pub struct FileProgDelete {
pub total_bytes: u64,
pub processed_bytes: u64,
pub collected: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgDelete> for TaskSummary {
@ -202,33 +168,21 @@ impl From<FileProgDelete> for TaskSummary {
}
}
impl FileProgDelete {
pub fn cooked(self) -> bool {
self.collected == Some(true) && self.success_files == self.total_files
impl Progress for FileProgDelete {
fn running(self) -> bool {
self.cooking_or_cleaning(
self.collected.is_none() || self.success_files + self.failed_files != self.total_files,
)
}
pub fn running(self) -> bool {
self.collected.is_none()
|| self.success_files + self.failed_files != self.total_files
|| (self.cleaned.is_none() && self.cooked())
}
fn cooked(self) -> bool { self.collected == Some(true) && self.success_files == self.total_files }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.collected == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> {
Some(if self.success() {
100.0
} else if self.failed() {
0.0
} else if self.total_bytes != 0 {
99.99f32.min(self.processed_bytes as f32 / self.total_bytes as f32 * 100.0)
} else {
99.99
})
fn percent(self) -> Option<f32> {
Some(self.byte_percent(self.processed_bytes, self.total_bytes))
}
}
@ -236,7 +190,7 @@ impl FileProgDelete {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FileProgTrash {
pub state: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgTrash> for TaskSummary {
@ -250,18 +204,14 @@ impl From<FileProgTrash> for TaskSummary {
}
}
impl FileProgTrash {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for FileProgTrash {
fn running(self) -> bool { self.cooking_or_cleaning(self.state.is_none()) }
pub fn running(self) -> bool { self.state.is_none() || (self.cleaned.is_none() && self.cooked()) }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.state == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> { None }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
}
// --- Download
@ -273,7 +223,7 @@ pub struct FileProgDownload {
pub total_bytes: u64,
pub processed_bytes: u64,
pub collected: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgDownload> for TaskSummary {
@ -287,33 +237,21 @@ impl From<FileProgDownload> for TaskSummary {
}
}
impl FileProgDownload {
pub fn cooked(self) -> bool {
self.collected == Some(true) && self.success_files == self.total_files
impl Progress for FileProgDownload {
fn running(self) -> bool {
self.cooking_or_cleaning(
self.collected.is_none() || self.success_files + self.failed_files != self.total_files,
)
}
pub fn running(self) -> bool {
self.collected.is_none()
|| self.success_files + self.failed_files != self.total_files
|| (self.cleaned.is_none() && self.cooked())
}
fn cooked(self) -> bool { self.collected == Some(true) && self.success_files == self.total_files }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.collected == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> {
Some(if self.success() {
100.0
} else if self.failed() {
0.0
} else if self.total_bytes != 0 {
99.99f32.min(self.processed_bytes as f32 / self.total_bytes as f32 * 100.0)
} else {
99.99
})
fn percent(self) -> Option<f32> {
Some(self.byte_percent(self.processed_bytes, self.total_bytes))
}
}
@ -326,7 +264,7 @@ pub struct FileProgUpload {
pub total_bytes: u64,
pub processed_bytes: u64,
pub collected: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<FileProgUpload> for TaskSummary {
@ -340,32 +278,20 @@ impl From<FileProgUpload> for TaskSummary {
}
}
impl FileProgUpload {
pub fn cooked(self) -> bool {
self.collected == Some(true) && self.success_files == self.total_files
impl Progress for FileProgUpload {
fn running(self) -> bool {
self.cooking_or_cleaning(
self.collected.is_none() || self.success_files + self.failed_files != self.total_files,
)
}
pub fn running(self) -> bool {
self.collected.is_none()
|| self.success_files + self.failed_files != self.total_files
|| (self.cleaned.is_none() && self.cooked())
}
fn cooked(self) -> bool { self.collected == Some(true) && self.success_files == self.total_files }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.collected == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> {
Some(if self.success() {
100.0
} else if self.failed() {
0.0
} else if self.total_bytes != 0 {
99.99f32.min(self.processed_bytes as f32 / self.total_bytes as f32 * 100.0)
} else {
99.99
})
fn percent(self) -> Option<f32> {
Some(self.byte_percent(self.processed_bytes, self.total_bytes))
}
}

View file

@ -2,7 +2,7 @@ mod macros;
yazi_macro::mod_pub!(fetch file hook plugin preload process size);
yazi_macro::mod_flat!(behavior ongoing op out progress proxy scheduler snap summary task worker);
yazi_macro::mod_flat!(behavior cleanup ongoing op out progress proxy scheduler snap summary task worker);
const LOW: u8 = yazi_config::Priority::Low as u8;
const NORMAL: u8 = yazi_config::Priority::Normal as u8;

View file

@ -45,3 +45,30 @@ macro_rules! impl_from_prog {
)*
};
}
#[macro_export]
macro_rules! dispatch_progress {
($value:expr, $method:ident) => {
match $value {
// File
$crate::TaskProg::FileCopy(p) => $crate::Progress::$method(p),
$crate::TaskProg::FileCut(p) => $crate::Progress::$method(p),
$crate::TaskProg::FileLink(p) => $crate::Progress::$method(p),
$crate::TaskProg::FileHardlink(p) => $crate::Progress::$method(p),
$crate::TaskProg::FileDelete(p) => $crate::Progress::$method(p),
$crate::TaskProg::FileTrash(p) => $crate::Progress::$method(p),
$crate::TaskProg::FileDownload(p) => $crate::Progress::$method(p),
$crate::TaskProg::FileUpload(p) => $crate::Progress::$method(p),
// Plugin
$crate::TaskProg::PluginEntry(p) => $crate::Progress::$method(p),
// Prework
$crate::TaskProg::Fetch(p) => $crate::Progress::$method(p),
$crate::TaskProg::Preload(p) => $crate::Progress::$method(p),
$crate::TaskProg::Size(p) => $crate::Progress::$method(p),
// Process
$crate::TaskProg::ProcessBlock(p) => $crate::Progress::$method(p),
$crate::TaskProg::ProcessOrphan(p) => $crate::Progress::$method(p),
$crate::TaskProg::ProcessBg(p) => $crate::Progress::$method(p),
}
};
}

View file

@ -1,6 +1,6 @@
use serde::Serialize;
use crate::TaskSummary;
use crate::{Progress, TaskSummary};
// --- Entry
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
@ -19,16 +19,10 @@ impl From<PluginProgEntry> for TaskSummary {
}
}
impl PluginProgEntry {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for PluginProgEntry {
fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cooked() }
pub fn failed(self) -> bool { self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { None }
pub fn percent(self) -> Option<f32> { None }
fn failed(self) -> bool { self.state == Some(false) }
}

View file

@ -1,6 +1,6 @@
use yazi_runner::preloader::PreloadError;
use crate::{Task, TaskProg};
use crate::{CleanupState, Task, TaskProg};
#[derive(Debug)]
pub(crate) enum PreloadOut {
@ -25,7 +25,7 @@ impl PreloadOut {
task.log(reason);
}
Self::Clean => {
prog.cleaned = Some(true);
prog.cleaned = CleanupState::Success;
}
}
}

View file

@ -1,11 +1,11 @@
use serde::Serialize;
use crate::TaskSummary;
use crate::{CleanupState, Progress, TaskSummary};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct PreloadProg {
pub state: Option<bool>,
pub cleaned: Option<bool>,
pub cleaned: CleanupState,
}
impl From<PreloadProg> for TaskSummary {
@ -19,16 +19,12 @@ impl From<PreloadProg> for TaskSummary {
}
}
impl PreloadProg {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for PreloadProg {
fn running(self) -> bool { self.cooking_or_cleaning(self.state.is_none()) }
pub fn running(self) -> bool { self.state.is_none() || (self.cleaned.is_none() && self.cooked()) }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() }
fn failed(self) -> bool { self.cleaned.is_failed() || self.state == Some(false) }
pub fn failed(self) -> bool { self.cleaned == Some(false) || self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { self.cleaned }
pub fn percent(self) -> Option<f32> { None }
fn cleaned(self) -> Option<CleanupState> { Some(self.cleaned) }
}

View file

@ -1,6 +1,6 @@
use serde::Serialize;
use crate::TaskSummary;
use crate::{Progress, TaskSummary};
// --- Block
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
@ -19,18 +19,12 @@ impl From<ProcessProgBlock> for TaskSummary {
}
}
impl ProcessProgBlock {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for ProcessProgBlock {
fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cooked() }
pub fn failed(self) -> bool { self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { None }
pub fn percent(self) -> Option<f32> { None }
fn failed(self) -> bool { self.state == Some(false) }
}
// --- Orphan
@ -50,18 +44,12 @@ impl From<ProcessProgOrphan> for TaskSummary {
}
}
impl ProcessProgOrphan {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for ProcessProgOrphan {
fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cooked() }
pub fn failed(self) -> bool { self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { None }
pub fn percent(self) -> Option<f32> { None }
fn failed(self) -> bool { self.state == Some(false) }
}
// --- Bg
@ -81,16 +69,10 @@ impl From<ProcessProgBg> for TaskSummary {
}
}
impl ProcessProgBg {
pub fn cooked(self) -> bool { self.state == Some(true) }
impl Progress for ProcessProgBg {
fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
fn cooked(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.cooked() }
pub fn failed(self) -> bool { self.state == Some(false) }
pub fn cleaned(self) -> Option<bool> { None }
pub fn percent(self) -> Option<f32> { None }
fn failed(self) -> bool { self.state == Some(false) }
}

View file

@ -1,7 +1,57 @@
use serde::Serialize;
use crate::{TaskSummary, fetch::FetchProg, file::{FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, impl_from_prog, plugin::PluginProgEntry, preload::PreloadProg, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::SizeProg};
use crate::{CleanupState, TaskSummary, dispatch_progress, fetch::FetchProg, file::{FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, impl_from_prog, plugin::PluginProgEntry, preload::PreloadProg, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::SizeProg};
pub trait Progress: Copy {
// Whether the task is still cooking or cleaning.
fn running(self) -> bool;
// Whether the task succeeded, regardless cleanup.
// For tasks without a cleanup, this is the same as `success()`.
fn cooked(self) -> bool;
// Whether the task fully succeeded, including cleanup if applicable.
fn success(self) -> bool {
match self.cleaned() {
None | Some(CleanupState::Success) => self.cooked(),
Some(CleanupState::Pending | CleanupState::Failed) => false,
}
}
// Whether the task fully failed.
//
// For tasks with a collect phase, e.g. gathering files to copy:
// collect failed, or cleanup failed if applicable, regardless main work.
// For tasks without a collect phase:
// main work failed, or cleanup failed if applicable.
fn failed(self) -> bool;
// Cleanup state if the task has a cleanup phase, otherwise `None`.
fn cleaned(self) -> Option<CleanupState> { None }
// Optional percentage for UI display.
fn percent(self) -> Option<f32> { None }
// Helper for tasks that are still cooking or cleaning.
fn cooking_or_cleaning(self, cooking: bool) -> bool {
cooking || (self.cooked() && self.cleaned() == Some(CleanupState::Pending))
}
// Helper for byte-based progress calculations used by file transfer tasks.
fn byte_percent(self, processed_bytes: u64, total_bytes: u64) -> f32 {
if self.success() {
100.0
} else if self.failed() {
0.0
} else if total_bytes != 0 {
99.99f32.min(processed_bytes as f32 / total_bytes as f32 * 100.0)
} else {
99.99
}
}
}
// --- TaskProg
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(tag = "kind")]
pub enum TaskProg {
@ -69,151 +119,21 @@ impl From<TaskProg> for TaskSummary {
}
}
impl Progress for TaskProg {
fn running(self) -> bool { dispatch_progress!(self, running) }
fn cooked(self) -> bool { dispatch_progress!(self, cooked) }
fn success(self) -> bool { dispatch_progress!(self, success) }
fn failed(self) -> bool { dispatch_progress!(self, failed) }
fn cleaned(self) -> Option<CleanupState> { dispatch_progress!(self, cleaned) }
fn percent(self) -> Option<f32> { dispatch_progress!(self, percent) }
}
impl TaskProg {
pub fn cooked(self) -> bool {
match self {
// File
Self::FileCopy(p) => p.cooked(),
Self::FileCut(p) => p.cooked(),
Self::FileLink(p) => p.cooked(),
Self::FileHardlink(p) => p.cooked(),
Self::FileDelete(p) => p.cooked(),
Self::FileTrash(p) => p.cooked(),
Self::FileDownload(p) => p.cooked(),
Self::FileUpload(p) => p.cooked(),
// Plugin
Self::PluginEntry(p) => p.cooked(),
// Prework
Self::Fetch(p) => p.cooked(),
Self::Preload(p) => p.cooked(),
Self::Size(p) => p.cooked(),
// Process
Self::ProcessBlock(p) => p.cooked(),
Self::ProcessOrphan(p) => p.cooked(),
Self::ProcessBg(p) => p.cooked(),
}
}
pub fn running(self) -> bool {
match self {
// File
Self::FileCopy(p) => p.running(),
Self::FileCut(p) => p.running(),
Self::FileLink(p) => p.running(),
Self::FileHardlink(p) => p.running(),
Self::FileDelete(p) => p.running(),
Self::FileTrash(p) => p.running(),
Self::FileDownload(p) => p.running(),
Self::FileUpload(p) => p.running(),
// Plugin
Self::PluginEntry(p) => p.running(),
// Prework
Self::Fetch(p) => p.running(),
Self::Preload(p) => p.running(),
Self::Size(p) => p.running(),
// Process
Self::ProcessBlock(p) => p.running(),
Self::ProcessOrphan(p) => p.running(),
Self::ProcessBg(p) => p.running(),
}
}
pub fn success(self) -> bool {
match self {
// File
Self::FileCopy(p) => p.success(),
Self::FileCut(p) => p.success(),
Self::FileLink(p) => p.success(),
Self::FileHardlink(p) => p.success(),
Self::FileDelete(p) => p.success(),
Self::FileTrash(p) => p.success(),
Self::FileDownload(p) => p.success(),
Self::FileUpload(p) => p.success(),
// Plugin
Self::PluginEntry(p) => p.success(),
// Prework
Self::Fetch(p) => p.success(),
Self::Preload(p) => p.success(),
Self::Size(p) => p.success(),
// Process
Self::ProcessBlock(p) => p.success(),
Self::ProcessOrphan(p) => p.success(),
Self::ProcessBg(p) => p.success(),
}
}
pub fn failed(self) -> bool {
match self {
// File
Self::FileCopy(p) => p.failed(),
Self::FileCut(p) => p.failed(),
Self::FileLink(p) => p.failed(),
Self::FileHardlink(p) => p.failed(),
Self::FileDelete(p) => p.failed(),
Self::FileTrash(p) => p.failed(),
Self::FileDownload(p) => p.failed(),
Self::FileUpload(p) => p.failed(),
// Plugin
Self::PluginEntry(p) => p.failed(),
// Prework
Self::Fetch(p) => p.failed(),
Self::Preload(p) => p.failed(),
Self::Size(p) => p.failed(),
// Process
Self::ProcessBlock(p) => p.failed(),
Self::ProcessOrphan(p) => p.failed(),
Self::ProcessBg(p) => p.failed(),
}
}
pub fn cleaned(self) -> Option<bool> {
match self {
// File
Self::FileCopy(p) => p.cleaned(),
Self::FileCut(p) => p.cleaned(),
Self::FileLink(p) => p.cleaned(),
Self::FileHardlink(p) => p.cleaned(),
Self::FileDelete(p) => p.cleaned(),
Self::FileTrash(p) => p.cleaned(),
Self::FileDownload(p) => p.cleaned(),
Self::FileUpload(p) => p.cleaned(),
// Plugin
Self::PluginEntry(p) => p.cleaned(),
// Prework
Self::Fetch(p) => p.cleaned(),
Self::Preload(p) => p.cleaned(),
Self::Size(p) => p.cleaned(),
// Process
Self::ProcessBlock(p) => p.cleaned(),
Self::ProcessOrphan(p) => p.cleaned(),
Self::ProcessBg(p) => p.cleaned(),
}
}
pub fn percent(self) -> Option<f32> {
match self {
// File
Self::FileCopy(p) => p.percent(),
Self::FileCut(p) => p.percent(),
Self::FileLink(p) => p.percent(),
Self::FileHardlink(p) => p.percent(),
Self::FileDelete(p) => p.percent(),
Self::FileTrash(p) => p.percent(),
Self::FileDownload(p) => p.percent(),
Self::FileUpload(p) => p.percent(),
// Plugin
Self::PluginEntry(p) => p.percent(),
// Prework
Self::Fetch(p) => p.percent(),
Self::Preload(p) => p.percent(),
Self::Size(p) => p.percent(),
// Process
Self::ProcessBlock(p) => p.percent(),
Self::ProcessOrphan(p) => p.percent(),
Self::ProcessBg(p) => p.percent(),
}
}
pub(crate) fn is_user(self) -> bool {
match self {
// File

View file

@ -1,6 +1,6 @@
use serde::Serialize;
use crate::TaskSummary;
use crate::{Progress, TaskSummary};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct SizeProg {
@ -18,16 +18,10 @@ impl From<SizeProg> for TaskSummary {
}
}
impl SizeProg {
pub fn cooked(self) -> bool { self.done }
impl Progress for SizeProg {
fn running(self) -> bool { !self.done }
pub fn running(self) -> bool { !self.done }
fn cooked(self) -> bool { self.done }
pub fn success(self) -> bool { self.cooked() }
pub fn failed(self) -> bool { false }
pub fn cleaned(self) -> Option<bool> { None }
pub fn percent(self) -> Option<f32> { None }
fn failed(self) -> bool { false }
}

View file

@ -1,7 +1,7 @@
use ordered_float::OrderedFloat;
use serde::Serialize;
use crate::Ongoing;
use crate::{Ongoing, Progress};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct TaskSummary {

View file

@ -4,7 +4,7 @@ use parking_lot::Mutex;
use tokio::{select, sync::mpsc, task::JoinHandle};
use yazi_config::YAZI;
use crate::{LOW, Ongoing, 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, 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 {
@ -282,7 +282,7 @@ impl Worker {
op.out.reduce(task);
if !task.prog.cooked() && task.done.completed() != Some(false) {
continue; // Not cooked yet, also not canceled
} else if task.prog.cleaned() == Some(false) {
} else if task.prog.cleaned() == Some(CleanupState::Failed) {
continue; // Failed to clean up
} else if let Some(hook) = task.hook.take() {
me.hook.submit(hook, LOW);