refactor: replace Metadata with Cha in the file scheduler to improve interoperability with yazi-fs (#1785)

This commit is contained in:
三咲雅 · Misaki Masa 2024-10-15 20:38:34 +08:00 committed by GitHub
parent 3e8ac856cf
commit 097e5abb82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 129 additions and 116 deletions

View file

@ -136,7 +136,7 @@ impl Watcher {
}; };
let u = &file.url; let u = &file.url;
let eq = (!file.is_link() && fs::canonicalize(u).await.is_ok_and(|p| p == ***u)) let eq = (!file.is_linkable() && fs::canonicalize(u).await.is_ok_and(|p| p == ***u))
|| realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s); || realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s);
if !eq { if !eq {

View file

@ -53,7 +53,6 @@ impl Utils {
area, area,
inner, inner,
wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO }, wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO },
..Default::default()
})]; })];
emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager)); emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager));

View file

@ -1,10 +1,10 @@
use std::{borrow::Cow, collections::VecDeque, fs::Metadata, path::{Path, PathBuf}}; use std::{borrow::Cow, collections::VecDeque, path::{Path, PathBuf}};
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use tokio::{fs, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
use tracing::warn; use tracing::warn;
use yazi_config::TASKS; use yazi_config::TASKS;
use yazi_shared::fs::{Url, calculate_size, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to}; use yazi_shared::fs::{Cha, Url, calculate_size, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to};
use super::{FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}; use super::{FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash};
use crate::{LOW, NORMAL, TaskOp, TaskProg}; use crate::{LOW, NORMAL, TaskOp, TaskProg};
@ -26,7 +26,7 @@ impl File {
match op { match op {
FileOp::Paste(mut task) => { FileOp::Paste(mut task) => {
ok_or_not_found(fs::remove_file(&task.to).await)?; ok_or_not_found(fs::remove_file(&task.to).await)?;
let mut it = copy_with_progress(&task.from, &task.to, task.meta.as_ref().unwrap()); let mut it = copy_with_progress(&task.from, &task.to, task.cha.unwrap());
while let Some(res) = it.recv().await { while let Some(res) = it.recv().await {
match res { match res {
@ -58,14 +58,14 @@ impl File {
self.prog.send(TaskProg::Adv(task.id, 1, 0))?; self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
} }
FileOp::Link(task) => { FileOp::Link(task) => {
let meta = task.meta.as_ref().unwrap(); let cha = task.cha.unwrap();
let src = if task.resolve { let src = if task.resolve {
match fs::read_link(&task.from).await { match fs::read_link(&task.from).await {
Ok(p) => Cow::Owned(p), Ok(p) => Cow::Owned(p),
Err(e) if e.kind() == NotFound => { Err(e) if e.kind() == NotFound => {
warn!("Link task partially done: {task:?}"); warn!("Link task partially done: {task:?}");
return Ok(self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?); return Ok(self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?);
} }
Err(e) => Err(e)?, Err(e) => Err(e)?,
} }
@ -86,7 +86,7 @@ impl File {
} }
#[cfg(windows)] #[cfg(windows)]
{ {
if meta.is_dir() { if cha.is_dir() {
fs::symlink_dir(src, &task.to).await?; fs::symlink_dir(src, &task.to).await?;
} else { } else {
fs::symlink_file(src, &task.to).await?; fs::symlink_file(src, &task.to).await?;
@ -96,10 +96,10 @@ impl File {
if task.delete { if task.delete {
fs::remove_file(&task.from).await.ok(); fs::remove_file(&task.from).await.ok();
} }
self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?; self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?;
} }
FileOp::Hardlink(task) => { FileOp::Hardlink(task) => {
let meta = task.meta.as_ref().unwrap(); let cha = task.cha.unwrap();
let src = if !task.follow { let src = if !task.follow {
Cow::Borrowed(task.from.as_path()) Cow::Borrowed(task.from.as_path())
} else if let Ok(p) = fs::canonicalize(&task.from).await { } else if let Ok(p) = fs::canonicalize(&task.from).await {
@ -116,7 +116,7 @@ impl File {
v => v?, v => v?,
} }
self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?; self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?;
} }
FileOp::Delete(task) => { FileOp::Delete(task) => {
if let Err(e) = fs::remove_file(&task.target).await { if let Err(e) = fs::remove_file(&task.target).await {
@ -154,19 +154,19 @@ impl File {
return self.succ(task.id); return self.succ(task.id);
} }
if task.meta.is_none() { if task.cha.is_none() {
task.meta = Some(Self::metadata(&task.from, task.follow).await?); task.cha = Some(Self::cha(&task.from, task.follow).await?);
} }
let meta = task.meta.as_ref().unwrap(); let cha = task.cha.unwrap();
if !meta.is_dir() { if !cha.is_dir() {
let id = task.id; let id = task.id;
self.prog.send(TaskProg::New(id, meta.len()))?; self.prog.send(TaskProg::New(id, cha.len))?;
if meta.is_file() { if cha.is_linkable() {
self.queue(FileOp::Paste(task), LOW).await?;
} else if meta.is_symlink() {
self.queue(FileOp::Link(task.into()), NORMAL).await?; self.queue(FileOp::Link(task.into()), NORMAL).await?;
} else {
self.queue(FileOp::Paste(task), LOW).await?;
} }
return self.succ(id); return self.succ(id);
} }
@ -198,20 +198,20 @@ impl File {
let mut it = continue_unless_ok!(fs::read_dir(&src).await); let mut it = continue_unless_ok!(fs::read_dir(&src).await);
while let Ok(Some(entry)) = it.next_entry().await { while let Ok(Some(entry)) = it.next_entry().await {
let from = Url::from(entry.path()); let from = Url::from(entry.path());
let meta = continue_unless_ok!(Self::metadata(&from, task.follow).await); let cha = continue_unless_ok!(Self::cha_from(entry, &from, task.follow).await);
if meta.is_dir() { if cha.is_dir() {
dirs.push_back(from); dirs.push_back(from);
continue; continue;
} }
let to = dest.join(from.file_name().unwrap()); let to = dest.join(from.file_name().unwrap());
self.prog.send(TaskProg::New(task.id, meta.len()))?; self.prog.send(TaskProg::New(task.id, cha.len))?;
if meta.is_file() { if cha.is_linkable() {
self.queue(FileOp::Paste(task.spawn(from, to, meta)), LOW).await?; self.queue(FileOp::Link(task.spawn(from, to, cha).into()), NORMAL).await?;
} else if meta.is_symlink() { } else {
self.queue(FileOp::Link(task.spawn(from, to, meta).into()), NORMAL).await?; self.queue(FileOp::Paste(task.spawn(from, to, cha)), LOW).await?;
} }
} }
} }
@ -220,24 +220,24 @@ impl File {
pub async fn link(&self, mut task: FileOpLink) -> Result<()> { pub async fn link(&self, mut task: FileOpLink) -> Result<()> {
let id = task.id; let id = task.id;
if task.meta.is_none() { if task.cha.is_none() {
task.meta = Some(fs::symlink_metadata(&task.from).await?); task.cha = Some(Self::cha(&task.from, false).await?);
} }
self.prog.send(TaskProg::New(id, task.meta.as_ref().unwrap().len()))?; self.prog.send(TaskProg::New(id, task.cha.unwrap().len))?;
self.queue(FileOp::Link(task), NORMAL).await?; self.queue(FileOp::Link(task), NORMAL).await?;
self.succ(id) self.succ(id)
} }
pub async fn hardlink(&self, mut task: FileOpHardlink) -> Result<()> { pub async fn hardlink(&self, mut task: FileOpHardlink) -> Result<()> {
if task.meta.is_none() { if task.cha.is_none() {
task.meta = Some(Self::metadata(&task.from, task.follow).await?); task.cha = Some(Self::cha(&task.from, task.follow).await?);
} }
let meta = task.meta.as_ref().unwrap(); let cha = task.cha.unwrap();
if !meta.is_dir() { if !cha.is_dir() {
let id = task.id; let id = task.id;
self.prog.send(TaskProg::New(id, meta.len()))?; self.prog.send(TaskProg::New(id, cha.len))?;
self.queue(FileOp::Hardlink(task), NORMAL).await?; self.queue(FileOp::Hardlink(task), NORMAL).await?;
return self.succ(id); return self.succ(id);
} }
@ -269,16 +269,16 @@ impl File {
let mut it = continue_unless_ok!(fs::read_dir(&src).await); let mut it = continue_unless_ok!(fs::read_dir(&src).await);
while let Ok(Some(entry)) = it.next_entry().await { while let Ok(Some(entry)) = it.next_entry().await {
let from = Url::from(entry.path()); let from = Url::from(entry.path());
let meta = continue_unless_ok!(Self::metadata(&from, task.follow).await); let cha = continue_unless_ok!(Self::cha_from(entry, &from, task.follow).await);
if meta.is_dir() { if cha.is_dir() {
dirs.push_back(from); dirs.push_back(from);
continue; continue;
} }
let to = dest.join(from.file_name().unwrap()); let to = dest.join(from.file_name().unwrap());
self.prog.send(TaskProg::New(task.id, meta.len()))?; self.prog.send(TaskProg::New(task.id, cha.len))?;
self.queue(FileOp::Hardlink(task.spawn(from, to, meta)), NORMAL).await?; self.queue(FileOp::Hardlink(task.spawn(from, to, cha)), NORMAL).await?;
} }
} }
self.succ(task.id) self.succ(task.id)
@ -325,13 +325,18 @@ impl File {
} }
#[inline] #[inline]
async fn metadata(path: &Path, follow: bool) -> io::Result<Metadata> { async fn cha(path: &Path, follow: bool) -> io::Result<Cha> {
if !follow { let meta = fs::symlink_metadata(path).await?;
return fs::symlink_metadata(path).await; Ok(if follow { Cha::new(path, meta).await } else { Cha::new_nofollow(path, meta) })
} }
let meta = fs::metadata(path).await; #[inline]
if meta.is_ok() { meta } else { fs::symlink_metadata(path).await } async fn cha_from(entry: DirEntry, path: &Path, follow: bool) -> io::Result<Cha> {
Ok(if follow {
Cha::new(path, entry.metadata().await?).await
} else {
Cha::new_nofollow(path, entry.metadata().await?)
})
} }
} }

View file

@ -1,6 +1,4 @@
use std::fs::Metadata; use yazi_shared::fs::{Cha, Url};
use yazi_shared::fs::Url;
#[derive(Debug)] #[derive(Debug)]
pub enum FileOp { pub enum FileOp {
@ -29,19 +27,19 @@ pub struct FileOpPaste {
pub id: usize, pub id: usize,
pub from: Url, pub from: Url,
pub to: Url, pub to: Url,
pub meta: Option<Metadata>, pub cha: Option<Cha>,
pub cut: bool, pub cut: bool,
pub follow: bool, pub follow: bool,
pub retry: u8, pub retry: u8,
} }
impl FileOpPaste { impl FileOpPaste {
pub(super) fn spawn(&self, from: Url, to: Url, meta: Metadata) -> Self { pub(super) fn spawn(&self, from: Url, to: Url, cha: Cha) -> Self {
Self { Self {
id: self.id, id: self.id,
from, from,
to, to,
meta: Some(meta), cha: Some(cha),
cut: self.cut, cut: self.cut,
follow: self.follow, follow: self.follow,
retry: self.retry, retry: self.retry,
@ -55,7 +53,7 @@ pub struct FileOpLink {
pub id: usize, pub id: usize,
pub from: Url, pub from: Url,
pub to: Url, pub to: Url,
pub meta: Option<Metadata>, pub cha: Option<Cha>,
pub resolve: bool, pub resolve: bool,
pub relative: bool, pub relative: bool,
pub delete: bool, pub delete: bool,
@ -67,7 +65,7 @@ impl From<FileOpPaste> for FileOpLink {
id: value.id, id: value.id,
from: value.from, from: value.from,
to: value.to, to: value.to,
meta: value.meta, cha: value.cha,
resolve: true, resolve: true,
relative: false, relative: false,
delete: value.cut, delete: value.cut,
@ -81,13 +79,13 @@ pub struct FileOpHardlink {
pub id: usize, pub id: usize,
pub from: Url, pub from: Url,
pub to: Url, pub to: Url,
pub meta: Option<Metadata>, pub cha: Option<Cha>,
pub follow: bool, pub follow: bool,
} }
impl FileOpHardlink { impl FileOpHardlink {
pub(super) fn spawn(&self, from: Url, to: Url, meta: Metadata) -> Self { pub(super) fn spawn(&self, from: Url, to: Url, cha: Cha) -> Self {
Self { id: self.id, from, to, meta: Some(meta), follow: self.follow } Self { id: self.id, from, to, cha: Some(cha), follow: self.follow }
} }
} }

View file

@ -71,7 +71,7 @@ impl Scheduler {
pub fn file_cut(&self, from: Url, mut to: Url, force: bool) { pub fn file_cut(&self, from: Url, mut to: Url, force: bool) {
let mut ongoing = self.ongoing.lock(); let mut ongoing = self.ongoing.lock();
let id = ongoing.add(TaskKind::User, format!("Cut {:?} to {:?}", from, to)); let id = ongoing.add(TaskKind::User, format!("Cut {from} to {to}"));
if to.starts_with(&from) && to != from { if to.starts_with(&from) && to != from {
self.new_and_fail(id, "Cannot cut directory into itself").ok(); self.new_and_fail(id, "Cannot cut directory into itself").ok();
@ -99,7 +99,7 @@ impl Scheduler {
if !force { if !force {
to = unique_name(to).await?; to = unique_name(to).await?;
} }
file.paste(FileOpPaste { id, from, to, meta: None, cut: true, follow: false, retry: 0 }).await file.paste(FileOpPaste { id, from, to, cha: None, cut: true, follow: false, retry: 0 }).await
}); });
} }
@ -116,7 +116,7 @@ impl Scheduler {
if !force { if !force {
to = unique_name(to).await?; to = unique_name(to).await?;
} }
file.paste(FileOpPaste { id, from, to, meta: None, cut: false, follow, retry: 0 }).await file.paste(FileOpPaste { id, from, to, cha: None, cut: false, follow, retry: 0 }).await
}); });
} }
@ -129,7 +129,7 @@ impl Scheduler {
to = unique_name(to).await?; to = unique_name(to).await?;
} }
file file
.link(FileOpLink { id, from, to, meta: None, resolve: false, relative, delete: false }) .link(FileOpLink { id, from, to, cha: None, resolve: false, relative, delete: false })
.await .await
}); });
} }
@ -147,7 +147,7 @@ impl Scheduler {
if !force { if !force {
to = unique_name(to).await?; to = unique_name(to).await?;
} }
file.hardlink(FileOpHardlink { id, from, to, meta: None, follow }).await file.hardlink(FileOpHardlink { id, from, to, cha: None, follow }).await
}); });
} }

View file

@ -1,8 +1,10 @@
use std::{fs::{FileType, Metadata}, time::SystemTime}; use std::{fs::{FileType, Metadata}, path::Path, time::SystemTime};
use bitflags::bitflags; use bitflags::bitflags;
use yazi_macro::unix_either; use yazi_macro::unix_either;
use super::Urn;
bitflags! { bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChaKind: u8 { pub struct ChaKind: u8 {
@ -37,40 +39,40 @@ pub struct Cha {
impl From<Metadata> for Cha { impl From<Metadata> for Cha {
fn from(m: Metadata) -> Self { fn from(m: Metadata) -> Self {
let mut ck = ChaKind::empty(); let mut kind = ChaKind::empty();
if m.is_dir() { if m.is_dir() {
ck |= ChaKind::DIR; kind |= ChaKind::DIR;
} }
Self { Self {
kind: ck, kind,
len: m.len(), len: m.len(),
atime: m.accessed().ok(), atime: m.accessed().ok(),
btime: m.created().ok(), btime: m.created().ok(),
#[cfg(unix)] #[cfg(unix)]
ctime: { ctime: {
use std::{os::unix::fs::MetadataExt, time::{Duration, UNIX_EPOCH}}; use std::{os::unix::fs::MetadataExt, time::{Duration, UNIX_EPOCH}};
UNIX_EPOCH.checked_add(Duration::new(m.ctime() as u64, m.ctime_nsec() as u32)) UNIX_EPOCH.checked_add(Duration::new(m.ctime() as u64, m.ctime_nsec() as u32))
}, },
mtime: m.modified().ok(), mtime: m.modified().ok(),
#[cfg(unix)] #[cfg(unix)]
perm: { perm: {
use std::os::unix::prelude::PermissionsExt; use std::os::unix::prelude::PermissionsExt;
m.permissions().mode() as _ m.permissions().mode() as _
}, },
#[cfg(unix)] #[cfg(unix)]
uid: { uid: {
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
m.uid() as _ m.uid() as _
}, },
#[cfg(unix)] #[cfg(unix)]
gid: { gid: {
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
m.gid() as _ m.gid() as _
}, },
#[cfg(unix)] #[cfg(unix)]
nlink: { nlink: {
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
m.nlink() as _ m.nlink() as _
}, },
@ -89,7 +91,7 @@ impl From<FileType> for Cha {
kind |= ChaKind::DIR; kind |= ChaKind::DIR;
libc::S_IFDIR libc::S_IFDIR
} else if t.is_symlink() { } else if t.is_symlink() {
kind |= ChaKind::LINK; kind |= ChaKind::ORPHAN;
libc::S_IFLNK libc::S_IFLNK
} else if t.is_block_device() { } else if t.is_block_device() {
libc::S_IFBLK libc::S_IFBLK
@ -124,14 +126,43 @@ impl From<FileType> for Cha {
impl Cha { impl Cha {
#[inline] #[inline]
pub fn dummy() -> Self { Self { kind: ChaKind::DUMMY, ..Default::default() } } pub async fn new(path: &Path, mut meta: Metadata) -> Self {
let mut attached = ChaKind::empty();
if meta.is_symlink() {
meta = tokio::fs::metadata(path).await.unwrap_or(meta);
attached |= if meta.is_symlink() { ChaKind::ORPHAN } else { ChaKind::LINK };
}
let mut cha = Self::new_nofollow(path, meta);
cha.kind |= attached;
cha
}
#[inline] #[inline]
pub fn with_kind(mut self, kind: ChaKind) -> Self { pub fn new_nofollow(path: &Path, meta: Metadata) -> Self {
self.kind |= kind; let mut attached = ChaKind::empty();
self
#[cfg(unix)]
if Urn::new(path).is_hidden() {
attached |= ChaKind::HIDDEN;
}
#[cfg(windows)]
{
use std::os::windows::fs::MetadataExt;
if meta.file_attributes() & 2 != 0 {
attached |= ChaKind::HIDDEN;
}
}
let mut cha = Self::from(meta);
cha.kind |= attached;
cha
} }
#[inline]
pub fn dummy() -> Self { Self { kind: ChaKind::DUMMY, ..Default::default() } }
#[inline] #[inline]
pub fn hits(self, c: Self) -> bool { pub fn hits(self, c: Self) -> bool {
self.len == c.len self.len == c.len
@ -156,6 +187,9 @@ impl Cha {
#[inline] #[inline]
pub const fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) } pub const fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) }
#[inline]
pub const fn is_linkable(&self) -> bool { self.is_link() || self.is_orphan() }
#[inline] #[inline]
pub const fn is_dummy(&self) -> bool { self.kind.contains(ChaKind::DUMMY) } pub const fn is_dummy(&self) -> bool { self.kind.contains(ChaKind::DUMMY) }

View file

@ -4,7 +4,7 @@ use anyhow::Result;
use tokio::fs; use tokio::fs;
use super::{Urn, UrnBuf}; use super::{Urn, UrnBuf};
use crate::{fs::{Cha, ChaKind, Url}, theme::IconCache}; use crate::{fs::{Cha, Url}, theme::IconCache};
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct File { pub struct File {
@ -34,34 +34,13 @@ impl File {
} }
#[inline] #[inline]
pub async fn from_meta(url: Url, mut meta: Metadata) -> Self { pub async fn from_meta(url: Url, meta: Metadata) -> Self {
let mut ck = ChaKind::empty(); let link_to =
let (is_link, mut link_to) = (meta.is_symlink(), None); if meta.is_symlink() { fs::read_link(&url).await.map(Url::from).ok() } else { None };
if is_link { let cha = Cha::new(&url, meta).await;
meta = fs::metadata(&url).await.unwrap_or(meta);
link_to = fs::read_link(&url).await.map(Url::from).ok();
}
if is_link && meta.is_symlink() { Self { url, cha, link_to, icon: Default::default() }
ck |= ChaKind::ORPHAN;
} else if is_link {
ck |= ChaKind::LINK;
}
#[cfg(unix)]
if url.is_hidden() {
ck |= ChaKind::HIDDEN;
}
#[cfg(windows)]
{
use std::os::windows::fs::MetadataExt;
if meta.file_attributes() & 2 != 0 {
ck |= ChaKind::HIDDEN;
}
}
Self { url, cha: Cha::from(meta).with_kind(ck), link_to, icon: Default::default() }
} }
#[inline] #[inline]

View file

@ -1,8 +1,10 @@
use std::{borrow::Cow, collections::{HashMap, HashSet, VecDeque}, ffi::{OsStr, OsString}, fs::Metadata, path::{Path, PathBuf}}; use std::{borrow::Cow, collections::{HashMap, HashSet, VecDeque}, ffi::{OsStr, OsString}, path::{Path, PathBuf}};
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time};
use super::Cha;
#[inline] #[inline]
pub async fn must_exists(p: impl AsRef<Path>) -> bool { fs::symlink_metadata(p).await.is_ok() } pub async fn must_exists(p: impl AsRef<Path>) -> bool { fs::symlink_metadata(p).await.is_ok() }
@ -175,7 +177,7 @@ pub async fn calculate_size(path: &Path) -> u64 {
pub fn copy_with_progress( pub fn copy_with_progress(
from: &Path, from: &Path,
to: &Path, to: &Path,
meta: &Metadata, cha: Cha,
) -> mpsc::Receiver<Result<u64, io::Error>> { ) -> mpsc::Receiver<Result<u64, io::Error>> {
let (tx, rx) = mpsc::channel(1); let (tx, rx) = mpsc::channel(1);
let (tick_tx, mut tick_rx) = oneshot::channel(); let (tick_tx, mut tick_rx) = oneshot::channel();
@ -184,17 +186,17 @@ pub fn copy_with_progress(
let (from, to) = (from.to_owned(), to.to_owned()); let (from, to) = (from.to_owned(), to.to_owned());
let mut ft = std::fs::FileTimes::new(); let mut ft = std::fs::FileTimes::new();
meta.accessed().map(|t| ft = ft.set_accessed(t)).ok(); cha.atime.map(|t| ft = ft.set_accessed(t));
meta.modified().map(|t| ft = ft.set_modified(t)).ok(); cha.mtime.map(|t| ft = ft.set_modified(t));
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
use std::os::macos::fs::FileTimesExt; use std::os::macos::fs::FileTimesExt;
meta.created().map(|t| ft = ft.set_created(t)).ok(); cha.btime.map(|t| ft = ft.set_created(t));
} }
#[cfg(windows)] #[cfg(windows)]
{ {
use std::os::windows::fs::FileTimesExt; use std::os::windows::fs::FileTimesExt;
meta.created().map(|t| ft = ft.set_created(t)).ok(); cha.btime.map(|t| ft = ft.set_created(t));
} }
async move { async move {

View file

@ -170,10 +170,6 @@ impl Url {
#[inline] #[inline]
pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) } pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) }
#[cfg(unix)]
#[inline]
pub fn is_hidden(&self) -> bool { self.loc.urn().is_hidden() }
#[inline] #[inline]
pub fn rebase(&self, parent: &Path) -> Self { pub fn rebase(&self, parent: &Path) -> Self {
debug_assert!(self.is_regular()); debug_assert!(self.is_regular());