mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
perf: partial rendering progress and composite into a complete UI to reduce CPU consumption caused by frequent progress updates
This commit is contained in:
parent
a735b3234f
commit
e81ebce284
26 changed files with 193 additions and 83 deletions
|
|
@ -67,8 +67,8 @@ rules = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks]
|
[tasks]
|
||||||
micro_workers = 5
|
micro_workers = 10
|
||||||
macro_workers = 10
|
macro_workers = 25
|
||||||
bizarre_retry = 5
|
bizarre_retry = 5
|
||||||
image_alloc = 536870912 # 512MB
|
image_alloc = 536870912 # 512MB
|
||||||
image_bound = [ 0, 0 ]
|
image_bound = [ 0, 0 ]
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,3 @@ mod cancel;
|
||||||
mod inspect;
|
mod inspect;
|
||||||
mod open;
|
mod open;
|
||||||
mod toggle;
|
mod toggle;
|
||||||
mod update;
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
use yazi_shared::{emit, event::Exec, render, Layer};
|
|
||||||
|
|
||||||
use crate::tasks::{Tasks, TasksProgress};
|
|
||||||
|
|
||||||
pub struct Opt {
|
|
||||||
progress: TasksProgress,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Exec> for Opt {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(e: &Exec) -> Result<Self, Self::Error> { e.take_data().ok_or(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tasks {
|
|
||||||
pub fn _update(progress: TasksProgress) {
|
|
||||||
emit!(Call(Exec::call("update", vec![]).with_data(Opt { progress }).vec(), Layer::Tasks));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, opt: impl TryInto<Opt>) {
|
|
||||||
let Ok(opt) = opt.try_into() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.progress = opt.progress;
|
|
||||||
render!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,7 @@ use tokio::time::sleep;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use yazi_config::{manager::SortBy, open::Opener, plugin::{PluginRule, MAX_PRELOADERS}, popup::InputCfg, OPEN, PLUGIN};
|
use yazi_config::{manager::SortBy, open::Opener, plugin::{PluginRule, MAX_PRELOADERS}, popup::InputCfg, OPEN, PLUGIN};
|
||||||
use yazi_scheduler::{Scheduler, TaskSummary};
|
use yazi_scheduler::{Scheduler, TaskSummary};
|
||||||
use yazi_shared::{fs::{File, Url}, term::Term, MIME_DIR};
|
use yazi_shared::{emit, event::Exec, fs::{File, Url}, term::Term, Layer, MIME_DIR};
|
||||||
|
|
||||||
use super::{TasksProgress, TASKS_PADDING, TASKS_PERCENT};
|
use super::{TasksProgress, TASKS_PADDING, TASKS_PERCENT};
|
||||||
use crate::{folder::Files, input::Input};
|
use crate::{folder::Files, input::Input};
|
||||||
|
|
@ -35,7 +35,7 @@ impl Tasks {
|
||||||
let new = TasksProgress::from(&*running.read());
|
let new = TasksProgress::from(&*running.read());
|
||||||
if last != new {
|
if last != new {
|
||||||
last = new;
|
last = new;
|
||||||
Tasks::_update(new);
|
emit!(Call(Exec::call("update_progress", vec![]).with_data(new).vec(), Layer::App));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ mod quit;
|
||||||
mod render;
|
mod render;
|
||||||
mod resize;
|
mod resize;
|
||||||
mod stop;
|
mod stop;
|
||||||
|
mod update_progress;
|
||||||
|
|
|
||||||
|
|
@ -29,17 +29,17 @@ impl App {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut patches = vec![];
|
let mut patch = vec![];
|
||||||
for x in frame.area.left()..frame.area.right() {
|
for x in frame.area.left()..frame.area.right() {
|
||||||
for y in frame.area.top()..frame.area.bottom() {
|
for y in frame.area.top()..frame.area.bottom() {
|
||||||
let cell = frame.buffer.get(x, y);
|
let cell = frame.buffer.get(x, y);
|
||||||
if cell.skip {
|
if cell.skip {
|
||||||
patches.push((x, y, cell.clone()));
|
patch.push((x, y, cell.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
term.backend_mut().draw(patches.iter().map(|(x, y, cell)| (*x, *y, cell)))?;
|
term.backend_mut().draw(patch.iter().map(|(x, y, cell)| (*x, *y, cell)))?;
|
||||||
if let Some((x, y)) = self.cx.cursor() {
|
if let Some((x, y)) = self.cx.cursor() {
|
||||||
term.show_cursor()?;
|
term.show_cursor()?;
|
||||||
term.set_cursor(x, y)?;
|
term.set_cursor(x, y)?;
|
||||||
|
|
|
||||||
41
yazi-fm/src/app/commands/update_progress.rs
Normal file
41
yazi-fm/src/app/commands/update_progress.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
use ratatui::backend::Backend;
|
||||||
|
use yazi_core::tasks::TasksProgress;
|
||||||
|
use yazi_shared::event::Exec;
|
||||||
|
|
||||||
|
use crate::{app::App, components::Progress, lives::Lives};
|
||||||
|
|
||||||
|
pub struct Opt {
|
||||||
|
progress: TasksProgress,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Exec> for Opt {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(e: &Exec) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self { progress: e.take_data().ok_or(())? })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub(crate) fn update_progress(&mut self, opt: impl TryInto<Opt>) {
|
||||||
|
let Ok(opt) = opt.try_into() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cx.tasks.progress = opt.progress;
|
||||||
|
let Some(term) = &mut self.term else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
Lives::partial_scope(&self.cx, |_| {
|
||||||
|
for patch in Progress::partial_render(term.current_buffer_mut()) {
|
||||||
|
term.backend_mut().draw(patch.iter().map(|(x, y, cell)| (*x, *y, cell))).ok();
|
||||||
|
if let Some((x, y)) = self.cx.cursor() {
|
||||||
|
term.show_cursor().ok();
|
||||||
|
term.set_cursor(x, y).ok();
|
||||||
|
}
|
||||||
|
term.backend_mut().flush().ok();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
mod header;
|
mod header;
|
||||||
mod manager;
|
mod manager;
|
||||||
mod preview;
|
mod preview;
|
||||||
|
mod progress;
|
||||||
mod status;
|
mod status;
|
||||||
|
|
||||||
pub(super) use header::*;
|
pub(super) use header::*;
|
||||||
pub(super) use manager::*;
|
pub(super) use manager::*;
|
||||||
pub(super) use preview::*;
|
pub(super) use preview::*;
|
||||||
|
pub(super) use progress::*;
|
||||||
pub(super) use status::*;
|
pub(super) use status::*;
|
||||||
|
|
|
||||||
43
yazi-fm/src/components/progress.rs
Normal file
43
yazi-fm/src/components/progress.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use mlua::{AnyUserData, Table, TableExt};
|
||||||
|
use tracing::error;
|
||||||
|
use yazi_plugin::{cast_to_renderable, LUA};
|
||||||
|
|
||||||
|
pub(crate) struct Progress;
|
||||||
|
|
||||||
|
impl Progress {
|
||||||
|
pub(crate) fn partial_render(
|
||||||
|
buf: &mut ratatui::buffer::Buffer,
|
||||||
|
) -> Vec<Vec<(u16, u16, ratatui::buffer::Cell)>> {
|
||||||
|
let mut patches = vec![];
|
||||||
|
let mut f = || {
|
||||||
|
let comp: Table = LUA.globals().get("Progress")?;
|
||||||
|
for widget in comp.call_method::<_, Vec<AnyUserData>>("partial_render", ())? {
|
||||||
|
let Some(w) = cast_to_renderable(widget) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let area = w.area();
|
||||||
|
w.render(buf);
|
||||||
|
|
||||||
|
let mut patch = Vec::with_capacity(area.width as usize * area.height as usize);
|
||||||
|
for x in area.left()..area.right() {
|
||||||
|
for y in area.top()..area.bottom() {
|
||||||
|
patch.push((x, y, mem::take(buf.get_mut(x, y))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.reset();
|
||||||
|
patches.push(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = f() {
|
||||||
|
error!("{:?}", e);
|
||||||
|
}
|
||||||
|
patches
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -84,6 +84,7 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
on!(plugin);
|
on!(plugin);
|
||||||
on!(plugin_do);
|
on!(plugin_do);
|
||||||
|
on!(update_progress);
|
||||||
on!(stop);
|
on!(stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +193,6 @@ impl<'a> Executor<'a> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
on!(update);
|
|
||||||
on!(open);
|
on!(open);
|
||||||
on!(toggle, "close");
|
on!(toggle, "close");
|
||||||
on!(arrow);
|
on!(arrow);
|
||||||
|
|
|
||||||
|
|
@ -54,4 +54,20 @@ impl Lives {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn partial_scope<'a>(cx: &'a Ctx, f: impl FnOnce(&Scope<'a, 'a>)) {
|
||||||
|
let result = LUA.scope(|scope| {
|
||||||
|
LUA.globals().set(
|
||||||
|
"cx",
|
||||||
|
LUA.create_table_from([("tasks", super::Tasks::new(scope, &cx.tasks).make()?)])?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
f(scope);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
error!("{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
45
yazi-plugin/preset/components/progress.lua
Normal file
45
yazi-plugin/preset/components/progress.lua
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
Progress = {
|
||||||
|
area = ui.Rect.default,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Progress:render(area, offset)
|
||||||
|
self.area = ui.Rect {
|
||||||
|
x = math.max(0, area.w - offset - 21),
|
||||||
|
y = area.y,
|
||||||
|
w = math.max(0, math.min(20, area.w - offset - 1)),
|
||||||
|
h = 1,
|
||||||
|
}
|
||||||
|
return self:partial_render()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Progress bars usually need frequent updates to report the latest task progress.
|
||||||
|
-- We use `partial_render()` to partially render it when there is progress change,
|
||||||
|
-- which has almost no cost compared to a full render by `render()`.
|
||||||
|
--
|
||||||
|
-- However, at this time, we can only access `cx.tasks`. If you need certain data from the complete `cx`,
|
||||||
|
-- just cache it to `self` during `render()`, and read it in `partial_render()` - this process is referred to as "composition".
|
||||||
|
function Progress:partial_render()
|
||||||
|
local progress = cx.tasks.progress
|
||||||
|
if progress.total == 0 then
|
||||||
|
return { ui.Paragraph(self.area, {}) }
|
||||||
|
end
|
||||||
|
|
||||||
|
local gauge = ui.Gauge(self.area)
|
||||||
|
if progress.fail == 0 then
|
||||||
|
gauge = gauge:gauge_style(THEME.status.progress_normal)
|
||||||
|
else
|
||||||
|
gauge = gauge:gauge_style(THEME.status.progress_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
local percent = 99
|
||||||
|
if progress.found ~= 0 then
|
||||||
|
percent = math.min(99, ya.round(progress.processed * 100 / progress.found))
|
||||||
|
end
|
||||||
|
|
||||||
|
local left = progress.total - progress.succ
|
||||||
|
return {
|
||||||
|
gauge
|
||||||
|
:percent(percent)
|
||||||
|
:label(ui.Span(string.format("%3d%%, %d left", percent, left)):style(THEME.status.progress_label)),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
@ -108,47 +108,14 @@ function Status:position()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function Status:progress(area, offset)
|
|
||||||
local progress = cx.tasks.progress
|
|
||||||
local left = progress.total - progress.succ
|
|
||||||
if left == 0 then
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local gauge = ui.Gauge(ui.Rect {
|
|
||||||
x = math.max(0, area.w - offset - 21),
|
|
||||||
y = area.y,
|
|
||||||
w = math.max(0, math.min(20, area.w - offset - 1)),
|
|
||||||
h = 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
if progress.fail == 0 then
|
|
||||||
gauge = gauge:gauge_style(THEME.status.progress_normal)
|
|
||||||
else
|
|
||||||
gauge = gauge:gauge_style(THEME.status.progress_error)
|
|
||||||
end
|
|
||||||
|
|
||||||
local percent = 99
|
|
||||||
if progress.found ~= 0 then
|
|
||||||
percent = math.min(99, progress.processed * 100 / progress.found)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
gauge
|
|
||||||
:percent(percent)
|
|
||||||
:label(ui.Span(string.format("%3d%%, %d left", percent, left)):style(THEME.status.progress_label)),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Status:render(area)
|
function Status:render(area)
|
||||||
self.area = area
|
self.area = area
|
||||||
|
|
||||||
local left = ui.Line { self:mode(), self:size(), self:name() }
|
local left = ui.Line { self:mode(), self:size(), self:name() }
|
||||||
local right = ui.Line { self:permissions(), self:percentage(), self:position() }
|
local right = ui.Line { self:permissions(), self:percentage(), self:position() }
|
||||||
local progress = self:progress(area, right:width())
|
|
||||||
return {
|
return {
|
||||||
ui.Paragraph(area, { left }),
|
ui.Paragraph(area, { left }),
|
||||||
ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT),
|
ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT),
|
||||||
table.unpack(progress),
|
table.unpack(Progress:render(area, right:width())),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ local M = {}
|
||||||
|
|
||||||
function M:peek()
|
function M:peek()
|
||||||
local url = ya.file_cache(self)
|
local url = ya.file_cache(self)
|
||||||
if not url or not fs.symlink_metadata(url) then
|
if not url or not fs.cha(url) then
|
||||||
url = self.file.url
|
url = self.file.url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ function M:seek() end
|
||||||
|
|
||||||
function M:preload()
|
function M:preload()
|
||||||
local cache = ya.file_cache(self)
|
local cache = ya.file_cache(self)
|
||||||
if not cache or fs.symlink_metadata(cache) then
|
if not cache or fs.cha(cache) then
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ end
|
||||||
|
|
||||||
function M:preload()
|
function M:preload()
|
||||||
local cache = ya.file_cache(self)
|
local cache = ya.file_cache(self)
|
||||||
if not cache or fs.symlink_metadata(cache) then
|
if not cache or fs.cha(cache) then
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ function M:preload()
|
||||||
end
|
end
|
||||||
|
|
||||||
local cache = ya.file_cache(self)
|
local cache = ya.file_cache(self)
|
||||||
if not cache or fs.symlink_metadata(cache) then
|
if not cache or fs.cha(cache) then
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ function ya.clamp(min, x, max)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ya.round(x) return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) end
|
||||||
|
|
||||||
function ya.flat(t)
|
function ya.flat(t)
|
||||||
local r = {}
|
local r = {}
|
||||||
for _, v in ipairs(t) do
|
for _, v in ipairs(t) do
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,8 @@ impl UserData for Bar {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderable for Bar {
|
impl Renderable for Bar {
|
||||||
|
fn area(&self) -> ratatui::layout::Rect { self.area }
|
||||||
|
|
||||||
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
||||||
if self.area.area() == 0 {
|
if self.area.area() == 0 {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,8 @@ impl UserData for Border {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderable for Border {
|
impl Renderable for Border {
|
||||||
|
fn area(&self) -> ratatui::layout::Rect { self.area }
|
||||||
|
|
||||||
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
||||||
let mut block =
|
let mut block =
|
||||||
ratatui::widgets::Block::default().borders(self.position).border_type(self.type_);
|
ratatui::widgets::Block::default().borders(self.position).border_type(self.type_);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ pub fn init(lua: &Lua) -> mlua::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderable {
|
pub trait Renderable {
|
||||||
|
fn area(&self) -> ratatui::layout::Rect;
|
||||||
|
|
||||||
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer);
|
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer);
|
||||||
|
|
||||||
fn clone_render(&self, buf: &mut ratatui::buffer::Buffer);
|
fn clone_render(&self, buf: &mut ratatui::buffer::Buffer);
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,8 @@ impl UserData for Gauge {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderable for Gauge {
|
impl Renderable for Gauge {
|
||||||
|
fn area(&self) -> ratatui::layout::Rect { self.area }
|
||||||
|
|
||||||
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
||||||
let mut gauge = ratatui::widgets::Gauge::default();
|
let mut gauge = ratatui::widgets::Gauge::default();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ impl List {
|
||||||
impl UserData for List {}
|
impl UserData for List {}
|
||||||
|
|
||||||
impl Renderable for List {
|
impl Renderable for List {
|
||||||
|
fn area(&self) -> ratatui::layout::Rect { self.area }
|
||||||
|
|
||||||
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
||||||
self.inner.render(self.area, buf);
|
self.inner.render(self.area, buf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,8 @@ impl UserData for Paragraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderable for Paragraph {
|
impl Renderable for Paragraph {
|
||||||
|
fn area(&self) -> ratatui::layout::Rect { self.area }
|
||||||
|
|
||||||
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
|
||||||
let mut p = ratatui::widgets::Paragraph::new(self.text);
|
let mut p = ratatui::widgets::Paragraph::new(self.text);
|
||||||
if let Some(style) = self.style {
|
if let Some(style) = self.style {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use mlua::Lua;
|
use mlua::{IntoLua, Lua, Value};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
use crate::bindings::{Cast, Cha, UrlRef};
|
use crate::bindings::{Cast, Cha, UrlRef};
|
||||||
|
|
@ -9,20 +9,29 @@ pub fn install(lua: &Lua) -> mlua::Result<()> {
|
||||||
lua.create_table_from([
|
lua.create_table_from([
|
||||||
(
|
(
|
||||||
"write",
|
"write",
|
||||||
lua.create_async_function(|_, (url, data): (UrlRef, mlua::String)| async move {
|
lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move {
|
||||||
Ok(fs::write(&*url, data).await.is_ok())
|
Ok(match fs::write(&*url, data).await {
|
||||||
|
Ok(_) => (Value::Boolean(true), Value::Nil),
|
||||||
|
Err(e) => (Value::Boolean(false), e.raw_os_error().into_lua(lua)?),
|
||||||
|
})
|
||||||
})?,
|
})?,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"metadata",
|
"cha",
|
||||||
lua.create_async_function(|lua, url: UrlRef| async move {
|
lua.create_async_function(|lua, url: UrlRef| async move {
|
||||||
fs::metadata(&*url).await.ok().map(|m| Cha::cast(lua, m)).transpose()
|
Ok(match fs::symlink_metadata(&*url).await {
|
||||||
|
Ok(m) => (Cha::cast(lua, m)?.into_lua(lua)?, Value::Nil),
|
||||||
|
Err(e) => (Value::Nil, e.raw_os_error().into_lua(lua)?),
|
||||||
|
})
|
||||||
})?,
|
})?,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"symlink_metadata",
|
"cha_follow",
|
||||||
lua.create_async_function(|lua, url: UrlRef| async move {
|
lua.create_async_function(|lua, url: UrlRef| async move {
|
||||||
fs::symlink_metadata(&*url).await.ok().map(|m| Cha::cast(lua, m)).transpose()
|
Ok(match fs::metadata(&*url).await {
|
||||||
|
Ok(m) => (Cha::cast(lua, m)?.into_lua(lua)?, Value::Nil),
|
||||||
|
Err(e) => (Value::Nil, e.raw_os_error().into_lua(lua)?),
|
||||||
|
})
|
||||||
})?,
|
})?,
|
||||||
),
|
),
|
||||||
])?,
|
])?,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub fn init() {
|
||||||
lua.load(include_str!("../preset/components/manager.lua")).exec()?;
|
lua.load(include_str!("../preset/components/manager.lua")).exec()?;
|
||||||
lua.load(include_str!("../preset/components/parent.lua")).exec()?;
|
lua.load(include_str!("../preset/components/parent.lua")).exec()?;
|
||||||
lua.load(include_str!("../preset/components/preview.lua")).exec()?;
|
lua.load(include_str!("../preset/components/preview.lua")).exec()?;
|
||||||
|
lua.load(include_str!("../preset/components/progress.lua")).exec()?;
|
||||||
lua.load(include_str!("../preset/components/status.lua")).exec()?;
|
lua.load(include_str!("../preset/components/status.lua")).exec()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ pub fn copy_with_progress(from: &Path, to: &Path) -> mpsc::Receiver<Result<u64,
|
||||||
select! {
|
select! {
|
||||||
res = &mut tick_rx => exit = Some(res.unwrap()),
|
res = &mut tick_rx => exit = Some(res.unwrap()),
|
||||||
_ = tx.closed() => break,
|
_ = tx.closed() => break,
|
||||||
_ = time::sleep(time::Duration::from_secs(1)) => (),
|
_ = time::sleep(time::Duration::from_secs(3)) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
match exit {
|
match exit {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue