Try another way

This commit is contained in:
sxyazi 2024-06-23 22:34:48 +08:00
parent 485e4d6974
commit 2c9ab2d930
No known key found for this signature in database
6 changed files with 52 additions and 76 deletions

7
Cargo.lock generated
View file

@ -2474,12 +2474,6 @@ dependencies = [
"winsafe",
]
[[package]]
name = "widestring"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
[[package]]
name = "winapi"
version = "0.3.9"
@ -2970,7 +2964,6 @@ dependencies = [
"serde",
"shell-words",
"tokio",
"widestring",
"windows-sys 0.52.0",
]

View file

@ -1 +1 @@
{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp"," Überzug"," Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","widestring"],"language":"en"}
{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp"," Überzug"," Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE"],"version":"0.2"}

View file

@ -84,16 +84,7 @@ impl Manager {
for (o, n) in todo {
let (old, new) = (root.join(&o), root.join(&n));
let overwrite_safe;
#[cfg(windows)]
{
overwrite_safe = yazi_shared::fs::rename_without_overwriting(&old, &new).await.is_ok();
}
#[cfg(unix)]
{
overwrite_safe = yazi_shared::fs::are_paths_equal(&old, &new).await;
}
if maybe_exists(&new).await && !overwrite_safe {
if maybe_exists(&new).await {
failed.push((o, n, anyhow!("Destination already exists")));
} else {
#[cfg(unix)]

View file

@ -62,46 +62,31 @@ impl Manager {
}
let new = hovered.parent().unwrap().join(name);
let overwrite_safe;
#[cfg(windows)]
{
overwrite_safe = yazi_shared::fs::rename_without_overwriting(&hovered, &new).await.is_ok();
}
#[cfg(unix)]
{
overwrite_safe = yazi_shared::fs::are_paths_equal(&hovered, &new).await;
}
if opt.force || !maybe_exists(&new).await || overwrite_safe {
Self::rename_do(tab, hovered, Url::from(new), overwrite_safe).await.ok();
if opt.force || !maybe_exists(&new).await {
Self::rename_do(tab, hovered, Url::from(new)).await.ok();
return;
}
let mut result = InputProxy::show(InputCfg::overwrite());
if let Some(Ok(choice)) = result.recv().await {
if choice == "y" || choice == "Y" {
Self::rename_do(tab, hovered, Url::from(new), false).await.ok();
Self::rename_do(tab, hovered, Url::from(new)).await.ok();
}
};
});
}
async fn rename_do(tab: usize, old: Url, new: Url, overwrite_safe: bool) -> Result<()> {
async fn rename_do(tab: usize, old: Url, new: Url) -> Result<()> {
let Some(p_old) = old.parent_url() else { return Ok(()) };
let Some(p_new) = new.parent_url() else { return Ok(()) };
let _permit = WATCHER.acquire().await.unwrap();
if overwrite_safe {
if !cfg!(windows) {
fs::rename(&old, &new).await?;
}
} else {
let overwritten = symlink_realpath(&new).await;
fs::rename(&old, &new).await?;
let overwritten = symlink_realpath(&new).await;
fs::rename(&old, &new).await?;
if let Ok(p) = overwritten {
ok_or_not_found(fs::rename(&p, &new).await)?;
FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit();
}
if let Ok(p) = overwritten {
ok_or_not_found(fs::rename(&p, &new).await)?;
FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit();
}
Pubsub::pub_from_rename(tab, &old, &new);

View file

@ -22,8 +22,9 @@ regex = "1.10.5"
serde = { version = "1.0.203", features = [ "derive" ] }
shell-words = "1.1.0"
tokio = { version = "1.38.0", features = [ "full" ] }
widestring = "1.1.0"
windows-sys = { version = "0.52.0", features = ["Win32_Foundation"] }
[target."cfg(unix)".dependencies]
libc = "0.2.155"
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.52.0", features = [ "Win32_Storage_FileSystem" ] }

View file

@ -23,42 +23,48 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> {
}
}
#[inline]
#[cfg(unix)]
pub async fn are_paths_equal(old: impl AsRef<Path>, new: impl AsRef<Path>) -> bool {
if let (Ok(canonical_old), Ok(canonical_new)) =
(fs::canonicalize(&old).await, fs::canonicalize(&new).await)
{
if let (Ok(old), Ok(new)) =
(fs::metadata(&canonical_old).await, fs::metadata(&canonical_new).await)
{
use std::os::unix::fs::MetadataExt;
old.ino() == new.ino() && old.dev() == new.dev()
} else {
false
}
} else {
false
}
pub async fn paths_to_same_file(a: &Path, b: &Path) -> io::Result<bool> {
use std::os::unix::fs::MetadataExt;
let (a, b) = (fs::symlink_metadata(a).await?, fs::symlink_metadata(b).await?);
Ok(a.ino() == b.ino() && a.dev() == b.dev())
}
#[inline]
#[cfg(windows)]
pub async fn rename_without_overwriting(
old: impl AsRef<Path>,
new: impl AsRef<Path>,
) -> io::Result<()> {
use widestring::U16CString;
use windows_sys::Win32::Storage::FileSystem::MoveFileExW;
let old = U16CString::from_os_str(old.as_ref().as_os_str())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?;
let new = U16CString::from_os_str(new.as_ref().as_os_str())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?;
tokio::task::spawn_blocking(move || {
let result = unsafe { MoveFileExW(old.as_ptr(), new.as_ptr(), 0) };
if result != 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
})
.await?
pub async fn paths_to_same_file(a: &Path, b: &Path) -> std::io::Result<bool> {
use std::{ffi::OsString, os::windows::{ffi::OsStringExt, io::AsRawHandle}, path::{Path, PathBuf}};
use windows_sys::Win32::{Foundation::{HANDLE, MAX_PATH}, Storage::FileSystem::{GetFinalPathNameByHandleW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, VOLUME_NAME_DOS}};
async fn final_name(p: &Path) -> std::io::Result<PathBuf> {
let file = tokio::fs::OpenOptions::new()
.access_mode(0)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)
.open(p)
.await?;
let mut buf = [0u16; MAX_PATH as usize];
tokio::task::spawn_blocking(move || {
let len = unsafe {
GetFinalPathNameByHandleW(
file.as_raw_handle() as HANDLE,
buf.as_mut_ptr(),
buf.len() as u32,
VOLUME_NAME_DOS,
)
};
if len == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(PathBuf::from(OsString::from_wide(&buf[0..len as usize])))
}
})
.await?
}
Ok(final_name(a).await? == final_name(b).await?)
}
pub async fn symlink_realpath(path: &Path) -> Result<PathBuf> {