fix!: open command doesn't work under empty directories (#3226)

This commit is contained in:
三咲雅 misaki masa 2025-10-03 17:45:35 +08:00 committed by sxyazi
parent d8b0425a10
commit 149a5feffb
No known key found for this signature in database
45 changed files with 277 additions and 280 deletions

View file

@ -22,9 +22,7 @@ futures = { workspace = true }
hashbrown = { workspace = true }
parking_lot = { workspace = true }
russh = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true }
[target."cfg(unix)".dependencies]

View file

@ -1,30 +1,30 @@
use std::io;
use yazi_fs::cha::{Cha, ChaKind};
use yazi_shared::url::Url;
use yazi_shared::url::AsUrl;
use crate::provider;
pub trait VfsCha: Sized {
fn from_url<'a>(url: impl Into<Url<'a>>) -> impl Future<Output = io::Result<Self>>;
fn from_url(url: impl AsUrl) -> impl Future<Output = io::Result<Self>>;
fn from_follow<'a, U>(url: U, cha: Self) -> impl Future<Output = Self>
fn from_follow<U>(url: U, cha: Self) -> impl Future<Output = Self>
where
U: Into<Url<'a>>;
U: AsUrl;
}
impl VfsCha for Cha {
#[inline]
async fn from_url<'a>(url: impl Into<Url<'a>>) -> io::Result<Self> {
let url = url.into();
async fn from_url(url: impl AsUrl) -> io::Result<Self> {
let url = url.as_url();
Ok(Self::from_follow(url, provider::symlink_metadata(url).await?).await)
}
async fn from_follow<'a, U>(url: U, mut cha: Self) -> Self
async fn from_follow<U>(url: U, mut cha: Self) -> Self
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
let mut retain = cha.kind & (ChaKind::HIDDEN | ChaKind::SYSTEM);
if cha.is_link() {

View file

@ -3,12 +3,12 @@ use std::{ffi::OsString, io};
use tokio::{select, sync::{mpsc, oneshot}};
use yazi_fs::cha::Cha;
use yazi_macro::ok_or_not_found;
use yazi_shared::url::{Url, UrlBuf};
use yazi_shared::url::{AsUrl, UrlBuf};
use crate::provider;
#[inline]
pub async fn maybe_exists<'a>(url: impl Into<Url<'a>>) -> bool {
pub async fn maybe_exists(url: impl AsUrl) -> bool {
match provider::symlink_metadata(url).await {
Ok(_) => true,
Err(e) => e.kind() != io::ErrorKind::NotFound,
@ -16,7 +16,7 @@ pub async fn maybe_exists<'a>(url: impl Into<Url<'a>>) -> bool {
}
#[inline]
pub async fn must_be_dir<'a>(url: impl Into<Url<'a>>) -> bool {
pub async fn must_be_dir(url: impl AsUrl) -> bool {
provider::metadata(url).await.is_ok_and(|m| m.is_dir())
}

View file

@ -1,3 +1,5 @@
#![allow(clippy::if_same_then_else, clippy::unit_arg)]
yazi_macro::mod_pub!(provider);
yazi_macro::mod_flat!(cha file files fns op);

View file

@ -1,7 +1,7 @@
use std::{collections::VecDeque, io, time::{Duration, Instant}};
use yazi_fs::provider::{DirReader, FileHolder};
use yazi_shared::{Either, url::{Url, UrlBuf}};
use yazi_shared::{Either, url::{AsUrl, UrlBuf}};
use super::ReadDir;
@ -11,11 +11,11 @@ pub enum SizeCalculator {
}
impl SizeCalculator {
pub async fn new<'a, U>(url: U) -> io::Result<Self>
pub async fn new<U>(url: U) -> io::Result<Self>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
let cha = super::symlink_metadata(url).await?;
Ok(if cha.is_dir() {
Self::Dir(VecDeque::from([Either::Left(url.to_owned())]))
@ -24,9 +24,9 @@ impl SizeCalculator {
})
}
pub async fn total<'a, U>(url: U) -> io::Result<u64>
pub async fn total<U>(url: U) -> io::Result<u64>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let mut it = Self::new(url).await?;
let mut total = 0;

View file

@ -2,23 +2,22 @@ use std::{io, path::{Path, PathBuf}};
use tokio::io::{BufReader, BufWriter};
use yazi_fs::{cha::Cha, provider::{Provider, local::Local}};
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}};
use yazi_shared::{scheme::SchemeRef, url::{AsUrl, UrlBuf, UrlCow}};
use super::{Providers, ReadDir, RwFile};
pub async fn absolute<'a, U>(url: U) -> io::Result<UrlCow<'a>>
pub async fn absolute<'a, U>(url: &'a U) -> io::Result<UrlCow<'a>>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
Providers::new(url).await?.absolute(url).await
Providers::new(url.as_url()).await?.absolute(url).await
}
pub async fn calculate<'a, U>(url: U) -> io::Result<u64>
pub async fn calculate<U>(url: U) -> io::Result<u64>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
if let Some(path) = url.as_path() {
yazi_fs::provider::local::SizeCalculator::total(path).await
} else {
@ -26,11 +25,11 @@ where
}
}
pub async fn canonicalize<'a, U>(url: U) -> io::Result<UrlBuf>
pub async fn canonicalize<U>(url: U) -> io::Result<UrlBuf>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
let canon = Providers::new(url).await?.canonicalize(url.loc).await?;
Ok(match url.scheme {
@ -42,11 +41,11 @@ where
})
}
pub async fn casefold<'a, U>(url: U) -> io::Result<UrlBuf>
pub async fn casefold<U>(url: U) -> io::Result<UrlBuf>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
let fold = Providers::new(url).await?.casefold(url.loc).await?;
Ok(match url.scheme {
@ -58,12 +57,12 @@ where
})
}
pub async fn copy<'a, U, V>(from: U, to: V, cha: Cha) -> io::Result<u64>
pub async fn copy<U, V>(from: U, to: V, cha: Cha) -> io::Result<u64>
where
U: Into<Url<'a>>,
V: Into<Url<'a>>,
U: AsUrl,
V: AsUrl,
{
let (from, to): (Url, Url) = (from.into(), to.into());
let (from, to) = (from.as_url(), to.as_url());
match (from.as_path(), to.as_path()) {
(Some(from), Some(to)) => Local.copy(from, to, cha).await,
@ -85,36 +84,36 @@ where
}
}
pub async fn create<'a, U>(url: U) -> io::Result<RwFile>
pub async fn create<U>(url: U) -> io::Result<RwFile>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.create(url.loc).await
}
pub async fn create_dir<'a, U>(url: U) -> io::Result<()>
pub async fn create_dir<U>(url: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.create_dir(url.loc).await
}
pub async fn create_dir_all<'a, U>(url: U) -> io::Result<()>
pub async fn create_dir_all<U>(url: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.create_dir_all(url.loc).await
}
pub async fn hard_link<'a, U, V>(original: U, link: V) -> io::Result<()>
pub async fn hard_link<U, V>(original: U, link: V) -> io::Result<()>
where
U: Into<Url<'a>>,
V: Into<Url<'a>>,
U: AsUrl,
V: AsUrl,
{
let (original, link): (Url, Url) = (original.into(), link.into());
let (original, link) = (original.as_url(), link.as_url());
if original.scheme.covariant(link.scheme) {
Providers::new(original).await?.hard_link(original.loc, link.loc).await
} else {
@ -122,88 +121,88 @@ where
}
}
pub async fn identical<'a, U, V>(a: U, b: V) -> io::Result<bool>
pub async fn identical<U, V>(a: U, b: V) -> io::Result<bool>
where
U: Into<Url<'a>>,
V: Into<Url<'a>>,
U: AsUrl,
V: AsUrl,
{
if let (Some(a), Some(b)) = (a.into().as_path(), b.into().as_path()) {
if let (Some(a), Some(b)) = (a.as_url().as_path(), b.as_url().as_path()) {
yazi_fs::provider::local::identical(a, b).await
} else {
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
}
}
pub async fn metadata<'a, U>(url: U) -> io::Result<Cha>
pub async fn metadata<U>(url: U) -> io::Result<Cha>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.metadata(url.loc).await
}
pub async fn must_identical<'a, U, V>(a: U, b: V) -> bool
pub async fn must_identical<U, V>(a: U, b: V) -> bool
where
U: Into<Url<'a>>,
V: Into<Url<'a>>,
U: AsUrl,
V: AsUrl,
{
identical(a, b).await.unwrap_or(false)
}
pub async fn read_dir<'a, U>(url: U) -> io::Result<ReadDir>
pub async fn read_dir<U>(url: U) -> io::Result<ReadDir>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.read_dir(url.loc).await
}
pub async fn read_link<'a, U>(url: U) -> io::Result<PathBuf>
pub async fn read_link<U>(url: U) -> io::Result<PathBuf>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.read_link(url.loc).await
}
pub async fn remove_dir<'a, U>(url: U) -> io::Result<()>
pub async fn remove_dir<U>(url: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.remove_dir(url.loc).await
}
pub async fn remove_dir_all<'a, U>(url: U) -> io::Result<()>
pub async fn remove_dir_all<U>(url: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.remove_dir_all(url.loc).await
}
pub async fn remove_dir_clean<'a, U>(url: U) -> io::Result<()>
pub async fn remove_dir_clean<U>(url: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Ok(Providers::new(url).await?.remove_dir_clean(url.loc).await)
}
pub async fn remove_file<'a, U>(url: U) -> io::Result<()>
pub async fn remove_file<U>(url: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.remove_file(url.loc).await
}
pub async fn rename<'a, U, V>(from: U, to: V) -> io::Result<()>
pub async fn rename<U, V>(from: U, to: V) -> io::Result<()>
where
U: Into<Url<'a>>,
V: Into<Url<'a>>,
U: AsUrl,
V: AsUrl,
{
let (from, to): (Url, Url) = (from.into(), to.into());
let (from, to) = (from.as_url(), to.as_url());
if from.scheme.covariant(to.scheme) {
Providers::new(from).await?.rename(from.loc, to.loc).await
} else {
@ -211,52 +210,52 @@ where
}
}
pub async fn symlink<'a, U, F>(original: &Path, link: U, is_dir: F) -> io::Result<()>
pub async fn symlink<U, F>(original: &Path, link: U, is_dir: F) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
F: AsyncFnOnce() -> io::Result<bool>,
{
let link: Url = link.into();
let link = link.as_url();
Providers::new(link).await?.symlink(original, link.loc, is_dir).await
}
pub async fn symlink_dir<'a, U>(original: &Path, link: U) -> io::Result<()>
pub async fn symlink_dir<U>(original: &Path, link: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let link: Url = link.into();
let link = link.as_url();
Providers::new(link).await?.symlink_dir(original, link.loc).await
}
pub async fn symlink_file<'a, U>(original: &Path, link: U) -> io::Result<()>
pub async fn symlink_file<U>(original: &Path, link: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let link: Url = link.into();
let link = link.as_url();
Providers::new(link).await?.symlink_file(original, link.loc).await
}
pub async fn symlink_metadata<'a, U>(url: U) -> io::Result<Cha>
pub async fn symlink_metadata<U>(url: U) -> io::Result<Cha>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.symlink_metadata(url.loc).await
}
pub async fn trash<'a, U>(url: U) -> io::Result<()>
pub async fn trash<U>(url: U) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.trash(url.loc).await
}
pub async fn write<'a, U, C>(url: U, contents: C) -> io::Result<()>
pub async fn write<U, C>(url: U, contents: C) -> io::Result<()>
where
U: Into<Url<'a>>,
U: AsUrl,
C: AsRef<[u8]>,
{
let url: Url = url.into();
let url = url.as_url();
Providers::new(url).await?.write(url.loc, contents).await
}

View file

@ -2,7 +2,7 @@ use std::{io, path::{Path, PathBuf}, sync::Arc};
use yazi_config::vfs::{ProviderSftp, Vfs};
use yazi_fs::{cha::Cha, provider::{Provider, local::Local}};
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlCow}};
use yazi_shared::{scheme::SchemeRef, url::{AsUrl, Url, UrlCow}};
pub(super) struct Providers<'a>(Inner<'a>);
@ -32,9 +32,9 @@ impl Provider for Providers<'_> {
type Gate = super::Gate;
type ReadDir = super::ReadDir;
async fn absolute<'a, U>(&self, url: U) -> io::Result<UrlCow<'a>>
async fn absolute<'a, U>(&self, url: &'a U) -> io::Result<UrlCow<'a>>
where
U: Into<Url<'a>>,
U: AsUrl,
{
match self.0 {
Inner::Regular | Inner::Search(_) => Local.absolute(url).await,

View file

@ -5,7 +5,7 @@ use tokio::io::{BufReader, BufWriter};
use yazi_config::vfs::ProviderSftp;
use yazi_fs::provider::{DirReader, FileBuilder, FileHolder, Provider, local::Local};
use yazi_sftp::fs::{Attrs, Flags};
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}};
use yazi_shared::{scheme::SchemeRef, url::{AsUrl, UrlBuf, UrlCow}};
use super::Cha;
@ -24,11 +24,11 @@ impl Provider for Sftp {
type Gate = super::Gate;
type ReadDir = super::ReadDir;
async fn absolute<'a, U>(&self, url: U) -> io::Result<UrlCow<'a>>
async fn absolute<'a, U>(&self, url: &'a U) -> io::Result<UrlCow<'a>>
where
U: Into<Url<'a>>,
U: AsUrl,
{
let url: Url = url.into();
let url = url.as_url();
Ok(if url.is_absolute() {
url.into()
} else if let SchemeRef::Sftp(_) = url.scheme {