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

52
Cargo.lock generated
View file

@ -115,9 +115,9 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.11"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
@ -1917,9 +1917,9 @@ dependencies = [
[[package]]
name = "lua-src"
version = "548.1.1"
version = "548.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00bc4bd1f1d5c65b30717333cbec4fa7aa378978940a1bca62f404498d423233"
checksum = "bdc4e1aff422ad5f08cffb4719603dcdbc2be2307f4c1510d7aab74b7fa88ca8"
dependencies = [
"cc",
]
@ -2003,9 +2003,9 @@ dependencies = [
[[package]]
name = "mlua"
version = "0.11.3"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b3dd94c3c4dea0049b22296397040840a8f6b5b5229f438434ba82df402b42d"
checksum = "9be1c2bfc684b8a228fbaebf954af7a47a98ec27721986654a4cc2c40a20cc7e"
dependencies = [
"anyhow",
"bstr",
@ -2341,9 +2341,9 @@ dependencies = [
[[package]]
name = "ordered-float"
version = "5.0.0"
version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01"
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
dependencies = [
"num-traits",
"rand 0.8.5",
@ -2732,9 +2732,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.40"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
@ -2909,7 +2909,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror 2.0.16",
"thiserror 2.0.17",
]
[[package]]
@ -3517,7 +3517,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"thiserror 2.0.16",
"thiserror 2.0.17",
"walkdir",
]
@ -3557,11 +3557,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.16"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl 2.0.16",
"thiserror-impl 2.0.17",
]
[[package]]
@ -3577,9 +3577,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
version = "2.0.16"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
@ -3884,9 +3884,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "typenum"
version = "1.18.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicode-ident"
@ -4764,7 +4764,7 @@ dependencies = [
"anyhow",
"hashbrown 0.16.0",
"mlua",
"ordered-float 5.0.0",
"ordered-float 5.1.0",
"parking_lot",
"paste",
"serde",
@ -4879,7 +4879,7 @@ dependencies = [
"crossterm 0.29.0",
"hashbrown 0.16.0",
"mlua",
"ordered-float 5.0.0",
"ordered-float 5.1.0",
"serde",
"tokio",
"yazi-binding",
@ -4957,7 +4957,7 @@ dependencies = [
"libc",
"lru 0.16.1",
"mlua",
"ordered-float 5.0.0",
"ordered-float 5.1.0",
"parking_lot",
"scopeguard",
"serde",
@ -4998,7 +4998,7 @@ dependencies = [
"hashbrown 0.16.0",
"libc",
"memchr",
"ordered-float 5.0.0",
"ordered-float 5.1.0",
"parking_lot",
"percent-encoding",
"serde",
@ -5031,9 +5031,7 @@ dependencies = [
"hashbrown 0.16.0",
"parking_lot",
"russh",
"serde",
"tokio",
"toml 0.9.7",
"tracing",
"uzers",
"yazi-config",
@ -5105,9 +5103,9 @@ dependencies = [
[[package]]
name = "zeroize"
version = "1.8.1"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
name = "zune-core"

View file

@ -35,9 +35,9 @@ hashbrown = { version = "0.16.0", features = [ "serde" ] }
indexmap = { version = "2.11.4", features = [ "serde" ] }
libc = "0.2.176"
lru = "0.16.1"
mlua = { version = "0.11.3", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
mlua = { version = "0.11.4", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
objc = "0.2.7"
ordered-float = { version = "5.0.0", features = [ "serde" ] }
ordered-float = { version = "5.1.0", features = [ "serde" ] }
parking_lot = "0.12.4"
paste = "1.0.15"
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] }

View file

@ -4,7 +4,7 @@ use yazi_fs::File;
use yazi_macro::{act, succ};
use yazi_parser::mgr::{OpenDoOpt, OpenOpt};
use yazi_proxy::MgrProxy;
use yazi_shared::{data::Data, url::UrlCow};
use yazi_shared::data::Data;
use yazi_vfs::VfsFile;
use crate::{Actor, Ctx, mgr::Quit};
@ -17,27 +17,28 @@ impl Actor for Open {
const NAME: &str = "open";
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
act!(mgr:escape_visual, cx)?;
let Some(hovered) = cx.hovered().map(|h| h.url_owned()).map(UrlCow::from) else { succ!() };
if !opt.interactive && ARGS.chooser_file.is_some() {
succ!(if !opt.targets.is_empty() {
Quit::with_selected(opt.targets)
} else if opt.hovered {
Quit::with_selected([hovered])
Quit::with_selected(cx.hovered().map(|h| &h.url))
} else {
act!(mgr:escape_visual, cx)?;
Quit::with_selected(cx.tab().selected_or_hovered())
});
}
if opt.targets.is_empty() {
opt.targets = if opt.hovered {
vec![hovered.clone()]
cx.hovered().map(|h| vec![h.url.clone().into()]).unwrap_or_default()
} else {
act!(mgr:escape_visual, cx)?;
cx.tab().selected_or_hovered().cloned().map(Into::into).collect()
};
}
if opt.targets.is_empty() {
succ!();
}
let todo: Vec<_> = opt
.targets
@ -49,7 +50,7 @@ impl Actor for Open {
let cwd = cx.cwd().clone();
if todo.is_empty() {
return act!(mgr:open_do, cx, OpenDoOpt { cwd, hovered, targets: opt.targets, interactive: opt.interactive });
return act!(mgr:open_do, cx, OpenDoOpt { cwd, targets: opt.targets, interactive: opt.interactive });
}
let scheduler = cx.tasks.scheduler.clone();
@ -61,12 +62,7 @@ impl Actor for Open {
}
}
if scheduler.fetch_mimetype(files).await {
MgrProxy::open_do(OpenDoOpt {
cwd,
hovered,
targets: opt.targets,
interactive: opt.interactive,
});
MgrProxy::open_do(OpenDoOpt { cwd, targets: opt.targets, interactive: opt.interactive });
}
});
succ!();

View file

@ -5,7 +5,7 @@ use yazi_config::{YAZI, popup::PickCfg};
use yazi_macro::succ;
use yazi_parser::mgr::OpenDoOpt;
use yazi_proxy::{PickProxy, TasksProxy};
use yazi_shared::data::Data;
use yazi_shared::{data::Data, url::UrlCow};
use crate::{Actor, Ctx};
@ -30,7 +30,7 @@ impl Actor for OpenDo {
if targets.is_empty() {
succ!();
} else if !opt.interactive {
succ!(cx.tasks.process_from_files(opt.cwd, opt.hovered, targets));
succ!(cx.tasks.process_with_selected(opt.cwd, targets));
}
let openers: Vec<_> = YAZI.opener.all(YAZI.open.common(&targets).into_iter());
@ -39,10 +39,10 @@ impl Actor for OpenDo {
}
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
let urls = [UrlCow::default()].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
tokio::spawn(async move {
if let Ok(choice) = pick.await {
TasksProxy::open_with(Cow::Borrowed(openers[choice]), opt.cwd, urls);
TasksProxy::file_open(Cow::Borrowed(openers[choice]), opt.cwd, urls);
}
});
succ!();

View file

@ -77,6 +77,8 @@ impl Quit {
s
});
emit!(Quit(EventQuit { selected: Some(paths), ..Default::default() }));
if !paths.is_empty() {
emit!(Quit(EventQuit { selected: Some(paths), ..Default::default() }));
}
}
}

View file

@ -111,7 +111,7 @@ impl Actor for SearchStop {
succ!();
}
let rep = tab.history.remove_or(&tab.cwd().to_regular());
let rep = tab.history.remove_or(tab.cwd().to_regular());
drop(mem::replace(&mut tab.current, rep));
act!(mgr:hidden, cx)?;

View file

@ -37,7 +37,7 @@ impl Actor for Shell {
return;
}
TasksProxy::open_with(
TasksProxy::file_open(
Cow::Owned(OpenerRule {
run: opt.run.into_owned(),
block: opt.block,

View file

@ -5,15 +5,15 @@ use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct OpenWith;
pub struct FileOpen;
impl Actor for OpenWith {
impl Actor for FileOpen {
type Options = OpenWithOpt;
const NAME: &str = "open_with";
const NAME: &str = "file_open";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
succ!(cx.tasks.process_from_opener(
succ!(cx.tasks.process_with_opener(
opt.cwd,
opt.opener,
opt.targets.into_iter().map(|u| u.into_os_str2()).collect(),

View file

@ -1 +1 @@
yazi_macro::mod_flat!(arrow cancel close inspect open_with process_exec show update_succeed);
yazi_macro::mod_flat!(arrow cancel close file_open inspect process_open show update_succeed);

View file

@ -1,16 +1,16 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::tasks::ProcessExecOpt;
use yazi_parser::tasks::ProcessOpenOpt;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct ProcessExec;
pub struct ProcessOpen;
impl Actor for ProcessExec {
type Options = ProcessExecOpt;
impl Actor for ProcessOpen {
type Options = ProcessOpenOpt;
const NAME: &str = "process_exec";
const NAME: &str = "process_open";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
succ!(cx.tasks.scheduler.process_open(opt));

View file

@ -1,7 +1,7 @@
use std::ops::Deref;
use mlua::{AnyUserData, ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
use yazi_shared::{IntoOsStr, url::UrlCow};
use yazi_shared::{IntoOsStr, url::{AsUrl, UrlCow}};
use crate::{Urn, cached_field, deprecate};
@ -29,12 +29,16 @@ impl AsRef<yazi_shared::url::UrlBuf> for Url {
fn as_ref(&self) -> &yazi_shared::url::UrlBuf { &self.inner }
}
impl From<Url> for yazi_shared::url::UrlBuf {
fn from(value: Url) -> Self { value.inner }
impl AsUrl for Url {
fn as_url(&self) -> yazi_shared::url::Url<'_> { self.inner.as_url() }
}
impl<'a> From<&'a Url> for yazi_shared::url::Url<'a> {
fn from(value: &'a Url) -> Self { value.as_url() }
impl AsUrl for &Url {
fn as_url(&self) -> yazi_shared::url::Url<'_> { self.inner.as_url() }
}
impl From<Url> for yazi_shared::url::UrlBuf {
fn from(value: Url) -> Self { value.inner }
}
impl<'a> From<&'a Url> for UrlCow<'a> {

View file

@ -14,4 +14,4 @@ proc-macro = true
[dependencies]
# External dependencies
syn = { version = "2.0.106", features = [ "full" ] }
quote = "1.0.40"
quote = "1.0.41"

View file

@ -48,7 +48,7 @@ impl Open {
M: AsRef<str>,
{
let each: Vec<IndexSet<&str>> = targets
.into_iter()
.iter()
.map(|(u, m)| self.all(u, m).collect::<IndexSet<_>>())
.filter(|s| !s.is_empty())
.collect();

View file

@ -3,7 +3,7 @@ use std::str::FromStr;
use anyhow::Result;
use globset::GlobBuilder;
use serde::Deserialize;
use yazi_shared::{scheme::{SchemeCow, SchemeRef}, url::Url};
use yazi_shared::{scheme::{SchemeCow, SchemeRef}, url::AsUrl};
#[derive(Debug, Deserialize)]
#[serde(try_from = "String")]
@ -17,8 +17,8 @@ pub struct Pattern {
}
impl Pattern {
pub fn match_url<'a>(&self, url: impl Into<Url<'a>>, is_dir: bool) -> bool {
let url = url.into();
pub fn match_url(&self, url: impl AsUrl, is_dir: bool) -> bool {
let url = url.as_url();
if is_dir != self.is_dir {
return false;
@ -126,7 +126,7 @@ mod tests {
use super::*;
fn matches(glob: &str, url: &str) -> bool {
Pattern::from_str(glob).unwrap().match_url(&UrlCow::try_from(url).unwrap(), false)
Pattern::from_str(glob).unwrap().match_url(UrlCow::try_from(url).unwrap(), false)
}
#[cfg(unix)]

View file

@ -1,7 +1,7 @@
use std::ops::{Deref, DerefMut};
use hashbrown::HashMap;
use yazi_shared::url::{Url, UrlBuf};
use yazi_shared::url::{AsUrl, UrlBuf};
use super::Folder;
@ -19,8 +19,8 @@ impl DerefMut for History {
}
impl History {
pub fn remove_or<'a>(&mut self, url: impl Into<Url<'a>>) -> Folder {
let url = url.into();
pub fn remove_or(&mut self, url: impl AsUrl) -> Folder {
let url = url.as_url();
self.0.remove(&url).unwrap_or_else(|| Folder::from(url))
}
}

View file

@ -2,53 +2,47 @@ use std::{borrow::Cow, ffi::OsStr, mem};
use hashbrown::HashMap;
use yazi_config::{YAZI, opener::OpenerRule};
use yazi_parser::tasks::ProcessExecOpt;
use yazi_parser::tasks::ProcessOpenOpt;
use yazi_shared::url::{UrlBuf, UrlCow};
use super::Tasks;
impl Tasks {
pub fn process_from_files(
&self,
cwd: UrlBuf,
hovered: UrlCow<'static>,
targets: Vec<(UrlCow<'static>, &str)>,
) {
pub fn process_with_selected(&self, cwd: UrlBuf, targets: Vec<(UrlCow<'static>, &str)>) {
let mut openers = HashMap::new();
for (url, mime) in targets {
if let Some(opener) = YAZI.opener.first(YAZI.open.all(&url, mime)) {
openers.entry(opener).or_insert_with(|| vec![hovered.clone()]).push(url);
openers
.entry(opener)
.or_insert_with(|| vec![OsStr::new("").into()])
.push(url.into_os_str2());
}
}
for (opener, args) in openers {
self.process_from_opener(
cwd.clone(),
Cow::Borrowed(opener),
args.into_iter().map(|u| u.into_os_str2()).collect(),
);
self.process_with_opener(cwd.clone(), Cow::Borrowed(opener), args);
}
}
pub fn process_from_opener(
pub fn process_with_opener(
&self,
cwd: UrlBuf,
opener: Cow<'static, OpenerRule>,
mut args: Vec<Cow<'static, OsStr>>,
) {
if opener.spread {
self.scheduler.process_open(ProcessExecOpt { cwd, opener, args, done: None });
self.scheduler.process_open(ProcessOpenOpt { cwd, opener, args, done: None });
return;
}
if args.is_empty() {
return;
}
if args.len() == 2 {
self.scheduler.process_open(ProcessExecOpt { cwd, opener, args, done: None });
self.scheduler.process_open(ProcessOpenOpt { cwd, opener, args, done: None });
return;
}
let hovered = mem::take(&mut args[0]);
for target in args.into_iter().skip(1) {
self.scheduler.process_open(ProcessExecOpt {
self.scheduler.process_open(ProcessOpenOpt {
cwd: cwd.clone(),
opener: opener.clone(),
args: vec![hovered.clone(), target],

View file

@ -108,7 +108,7 @@ pub enum Spark<'a> {
SpotCopy(yazi_parser::spot::CopyOpt),
// Tasks
TasksProcessExec(yazi_parser::tasks::ProcessExecOpt),
TasksProcessOpen(yazi_parser::tasks::ProcessOpenOpt),
TasksUpdateSucceed(yazi_parser::tasks::UpdateSucceedOpt),
// Which
@ -231,7 +231,7 @@ impl<'a> IntoLua for Spark<'a> {
Self::SpotCopy(b) => b.into_lua(lua),
// Tasks
Self::TasksProcessExec(b) => b.into_lua(lua),
Self::TasksProcessOpen(b) => b.into_lua(lua),
Self::TasksUpdateSucceed(b) => b.into_lua(lua),
// Which
@ -324,7 +324,7 @@ try_from_spark!(notify::TickOpt, notify:tick);
try_from_spark!(pick::CloseOpt, pick:close);
try_from_spark!(pick::ShowOpt, pick:show);
try_from_spark!(spot::CopyOpt, spot:copy);
try_from_spark!(tasks::ProcessExecOpt, tasks:process_exec);
try_from_spark!(tasks::ProcessOpenOpt, tasks:process_open);
try_from_spark!(tasks::UpdateSucceedOpt, tasks:update_succeed);
try_from_spark!(which::CallbackOpt, which:callback);
try_from_spark!(which::ShowOpt, which:show);

View file

@ -164,8 +164,8 @@ impl<'a> Executor<'a> {
on!(arrow);
on!(inspect);
on!(cancel);
on!(open_with);
on!(process_exec);
on!(file_open);
on!(process_open);
match cmd.name.as_ref() {
// Help

View file

@ -2,7 +2,7 @@ use std::{ffi::OsStr, fs::Metadata, ops::Deref, time::{Duration, SystemTime, UNI
use anyhow::bail;
use yazi_macro::{unix_either, win_either};
use yazi_shared::url::Url;
use yazi_shared::url::AsUrl;
use super::ChaKind;
use crate::cha::{ChaMode, ChaType};
@ -52,15 +52,15 @@ impl Cha {
Self::from_bare(&meta).attach(ChaKind::hidden(name, &meta))
}
pub fn from_dummy<'a, U>(_url: U, r#type: Option<ChaType>) -> Self
pub fn from_dummy<U>(_url: U, r#type: Option<ChaType>) -> Self
where
U: Into<Url<'a>>,
U: AsUrl,
{
let mut kind = ChaKind::DUMMY;
let mode = r#type.map(ChaMode::from_bare).unwrap_or_default();
#[cfg(unix)]
if _url.into().urn().is_hidden() {
if _url.as_url().urn().is_hidden() {
kind |= ChaKind::HIDDEN;
}

View file

@ -1,6 +1,6 @@
use std::{io, path::{Path, PathBuf}};
use yazi_shared::url::{Url, UrlCow};
use yazi_shared::url::{AsUrl, UrlCow};
use crate::{cha::Cha, path::absolute_url, provider::Provider};
@ -12,11 +12,11 @@ impl Provider for Local {
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();
if url.scheme.is_virtual() {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Not a local URL"))
} else {

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, io, path::{Path, PathBuf}};
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
use yazi_macro::ok_or_not_found;
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlCow}};
use yazi_shared::{scheme::SchemeRef, url::{AsUrl, UrlCow}};
use crate::cha::{Cha, ChaType};
@ -11,9 +11,9 @@ pub trait Provider {
type Gate: FileBuilder<File = Self::File>;
type ReadDir: DirReader;
fn absolute<'a, U>(&self, url: U) -> impl Future<Output = io::Result<UrlCow<'a>>>
fn absolute<'a, U>(&self, url: &'a U) -> impl Future<Output = io::Result<UrlCow<'a>>>
where
U: Into<Url<'a>>;
U: AsUrl;
fn canonicalize<P>(&self, path: P) -> impl Future<Output = io::Result<PathBuf>>
where

View file

@ -4,7 +4,6 @@ use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
#[derive(Debug, Default)]
pub struct OpenDoOpt {
pub cwd: UrlBuf,
pub hovered: UrlCow<'static>,
pub targets: Vec<UrlCow<'static>>,
pub interactive: bool,
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(process_exec update_succeed);
yazi_macro::mod_flat!(process_open update_succeed);

View file

@ -8,25 +8,25 @@ use yazi_shared::{event::CmdCow, url::UrlBuf};
// --- Exec
#[derive(Debug)]
pub struct ProcessExecOpt {
pub struct ProcessOpenOpt {
pub cwd: UrlBuf,
pub opener: Cow<'static, OpenerRule>,
pub args: Vec<Cow<'static, OsStr>>,
pub done: Option<oneshot::Sender<()>>,
}
impl TryFrom<CmdCow> for ProcessExecOpt {
impl TryFrom<CmdCow> for ProcessOpenOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in ProcessExecOpt"))
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in ProcessOpenOpt"))
}
}
impl FromLua for ProcessExecOpt {
impl FromLua for ProcessOpenOpt {
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
}
impl IntoLua for ProcessExecOpt {
impl IntoLua for ProcessOpenOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -3,14 +3,14 @@ use std::{borrow::Cow, ffi::OsStr};
use tokio::sync::oneshot;
use yazi_config::opener::OpenerRule;
use yazi_macro::{emit, relay};
use yazi_parser::{mgr::OpenWithOpt, tasks::ProcessExecOpt};
use yazi_parser::{mgr::OpenWithOpt, tasks::ProcessOpenOpt};
use yazi_shared::url::{UrlBuf, UrlCow};
pub struct TasksProxy;
impl TasksProxy {
pub fn open_with(opener: Cow<'static, OpenerRule>, cwd: UrlBuf, targets: Vec<UrlCow<'static>>) {
emit!(Call(relay!(tasks:open_with).with_any("option", OpenWithOpt { opener, cwd, targets })));
pub fn file_open(opener: Cow<'static, OpenerRule>, cwd: UrlBuf, targets: Vec<UrlCow<'static>>) {
emit!(Call(relay!(tasks:file_open).with_any("option", OpenWithOpt { opener, cwd, targets })));
}
pub async fn process_exec(
@ -19,7 +19,7 @@ impl TasksProxy {
args: Vec<Cow<'static, OsStr>>,
) {
let (tx, rx) = oneshot::channel();
emit!(Call(relay!(tasks:process_exec).with_any("option", ProcessExecOpt {
emit!(Call(relay!(tasks:process_open).with_any("option", ProcessOpenOpt {
cwd,
opener,
args,

View file

@ -6,7 +6,7 @@ use tracing::warn;
use yazi_config::YAZI;
use yazi_fs::{cha::Cha, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{DirReader, FileHolder}};
use yazi_macro::ok_or_not_found;
use yazi_shared::url::{Url, UrlBuf, UrlCow};
use yazi_shared::url::{AsUrl, UrlBuf, UrlCow};
use yazi_vfs::{VfsCha, copy_with_progress, maybe_exists, provider::{self, DirEntry}};
use super::{FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
@ -289,8 +289,8 @@ impl File {
}
#[inline]
async fn cha<'a>(url: impl Into<Url<'a>>, follow: bool) -> io::Result<Cha> {
let url = url.into();
async fn cha(url: impl AsUrl, follow: bool) -> io::Result<Cha> {
let url = url.as_url();
let cha = provider::symlink_metadata(url).await?;
Ok(if follow { Cha::from_follow(url, cha).await } else { cha })
}

View file

@ -7,7 +7,7 @@ use tokio::{select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::Join
use yazi_config::{YAZI, plugin::{Fetcher, Preloader}};
use yazi_dds::Pump;
use yazi_fs::FsUrl;
use yazi_parser::{app::PluginOpt, tasks::ProcessExecOpt};
use yazi_parser::{app::PluginOpt, tasks::ProcessOpenOpt};
use yazi_proxy::TasksProxy;
use yazi_shared::{Id, Throttle, url::UrlBuf};
use yazi_vfs::{must_be_dir, provider, unique_name};
@ -310,7 +310,7 @@ impl Scheduler {
}
}
pub fn process_open(&self, ProcessExecOpt { cwd, opener, args, done }: ProcessExecOpt) {
pub fn process_open(&self, ProcessOpenOpt { cwd, opener, args, done }: ProcessOpenOpt) {
let name = {
let args = args.iter().map(|a| a.to_string_lossy()).collect::<Vec<_>>().join(" ");
if args.is_empty() {

View file

@ -33,7 +33,7 @@ impl From<Error> for std::io::Error {
Error::IO(e) => e,
Error::Serde(e) => Self::new(std::io::ErrorKind::InvalidData, e),
Error::Status(status) => match status.code {
responses::StatusCode::Ok => Self::new(std::io::ErrorKind::Other, "unexpected OK"),
responses::StatusCode::Ok => Self::other("unexpected OK"),
responses::StatusCode::Eof => Self::from(std::io::ErrorKind::UnexpectedEof),
responses::StatusCode::NoSuchFile => Self::from(std::io::ErrorKind::NotFound),
responses::StatusCode::PermissionDenied => Self::from(std::io::ErrorKind::PermissionDenied),

View file

@ -78,7 +78,7 @@ impl Operator {
status.into()
}
pub async fn fsetstat<'a>(&self, handle: &str, attrs: &'a Attrs) -> Result<(), Error> {
pub async fn fsetstat(&self, handle: &str, attrs: &Attrs) -> Result<(), Error> {
let status: responses::Status = self.send(requests::FSetStat::new(handle, attrs)).await?;
status.into()
}

View file

@ -99,7 +99,7 @@ impl Cmd {
T: TryFrom<&'a Data>,
T::Error: Into<anyhow::Error>,
{
self.get(0).map_err(Into::into)
self.get(0)
}
pub fn second<'a, T>(&'a self) -> Result<T>
@ -107,7 +107,7 @@ impl Cmd {
T: TryFrom<&'a Data>,
T::Error: Into<anyhow::Error>,
{
self.get(1).map_err(Into::into)
self.get(1)
}
pub fn seq<'a, T>(&'a self) -> Vec<T>
@ -128,7 +128,7 @@ impl Cmd {
}
// --- Take
pub fn take<'a, T>(&mut self, name: impl Into<DataKey>) -> Result<T>
pub fn take<T>(&mut self, name: impl Into<DataKey>) -> Result<T>
where
T: TryFrom<Data>,
T::Error: Into<anyhow::Error>,
@ -140,7 +140,7 @@ impl Cmd {
}
}
pub fn take_first<'a, T>(&mut self) -> Result<T>
pub fn take_first<T>(&mut self) -> Result<T>
where
T: TryFrom<Data>,
T::Error: Into<anyhow::Error>,

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, hash::BuildHasher, p
use anyhow::Result;
use serde::{Deserialize, Serialize};
use crate::{loc::LocBuf, pool::Pool, scheme::{Scheme, SchemeRef}, url::{Components, Display, Encode, EncodeTilded, Uri, Url, UrlCow, Urn}};
use crate::{loc::LocBuf, pool::Pool, scheme::{Scheme, SchemeRef}, url::{AsUrl, Components, Display, Encode, EncodeTilded, Uri, Url, UrlCow, Urn}};
#[derive(Clone, Default, Eq, Hash, PartialEq)]
pub struct UrlBuf {
@ -108,18 +108,16 @@ impl UrlBuf {
pub fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
#[inline]
pub fn starts_with<'a>(&self, base: impl Into<Url<'a>>) -> bool {
self.as_url().starts_with(base)
}
pub fn starts_with(&self, base: impl AsUrl) -> bool { self.as_url().starts_with(base) }
#[inline]
pub fn ends_with<'a>(&self, child: impl Into<Url<'a>>) -> bool { self.as_url().ends_with(child) }
pub fn ends_with(&self, child: impl AsUrl) -> bool { self.as_url().ends_with(child) }
pub fn strip_prefix<'a>(&self, base: impl Into<Url<'a>>) -> Option<&Urn> {
pub fn strip_prefix(&self, base: impl AsUrl) -> Option<&Urn> {
use Scheme as S;
use SchemeRef as T;
let base = base.into();
let base = base.as_url();
let prefix = self.loc.strip_prefix(base.loc).ok()?;
Some(Urn::new(match (&self.scheme, base.scheme) {
@ -174,7 +172,7 @@ impl UrlBuf {
impl UrlBuf {
#[inline]
pub fn as_url(&self) -> Url<'_> { Url::from(self) }
pub fn as_url(&self) -> Url<'_> { Url { loc: self.loc.as_loc(), scheme: self.scheme.as_ref() } }
#[inline]
pub fn base(&self) -> Option<Url<'_>> { self.as_url().base() }

View file

@ -33,10 +33,6 @@ impl From<UrlBuf> for UrlCow<'_> {
fn from(value: UrlBuf) -> Self { Self::Owned { loc: value.loc, scheme: value.scheme.into() } }
}
impl<'a> From<&'a UrlCow<'a>> for Url<'a> {
fn from(value: &'a UrlCow<'a>) -> Self { value.as_url() }
}
impl From<UrlCow<'_>> for UrlBuf {
fn from(value: UrlCow<'_>) -> Self { value.into_owned() }
}

View file

@ -1,4 +1,4 @@
use crate::url::{Url, UrlCow};
use crate::url::{Url, UrlBuf, UrlCow};
pub trait AsUrl {
fn as_url(&self) -> Url<'_>;
@ -9,6 +9,21 @@ impl AsUrl for Url<'_> {
fn as_url(&self) -> Url<'_> { *self }
}
impl AsUrl for UrlBuf {
#[inline]
fn as_url(&self) -> Url<'_> { self.as_url() }
}
impl AsUrl for &UrlBuf {
#[inline]
fn as_url(&self) -> Url<'_> { (**self).as_url() }
}
impl AsUrl for &mut UrlBuf {
#[inline]
fn as_url(&self) -> Url<'_> { (**self).as_url() }
}
impl AsUrl for UrlCow<'_> {
#[inline]
fn as_url(&self) -> Url<'_> { self.as_url() }
@ -16,5 +31,13 @@ impl AsUrl for UrlCow<'_> {
impl AsUrl for &UrlCow<'_> {
#[inline]
fn as_url(&self) -> Url<'_> { (*self).as_url() }
fn as_url(&self) -> Url<'_> { (**self).as_url() }
}
impl<'a, T: AsUrl> From<&'a T> for Url<'a> {
fn from(value: &'a T) -> Self { value.as_url() }
}
impl<'a, T: AsUrl> From<&'a mut T> for Url<'a> {
fn from(value: &'a mut T) -> Self { value.as_url() }
}

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, path::Path};
use hashbrown::Equivalent;
use crate::{loc::{Loc, LocBuf}, scheme::SchemeRef, url::{Components, Encode, Uri, UrlBuf, Urn}};
use crate::{loc::{Loc, LocBuf}, scheme::SchemeRef, url::{AsUrl, Components, Encode, Uri, UrlBuf, Urn}};
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct Url<'a> {
@ -10,16 +10,6 @@ pub struct Url<'a> {
pub scheme: SchemeRef<'a>,
}
impl<'a> From<&'a UrlBuf> for Url<'a> {
fn from(value: &'a UrlBuf) -> Self {
Self { loc: value.loc.as_loc(), scheme: value.scheme.as_ref() }
}
}
impl<'a> From<&'a mut UrlBuf> for Url<'a> {
fn from(value: &'a mut UrlBuf) -> Self { Self::from(&*value) }
}
// --- Eq
impl PartialEq<UrlBuf> for Url<'_> {
fn eq(&self, other: &UrlBuf) -> bool { *self == other.as_url() }
@ -142,14 +132,14 @@ impl<'a> Url<'a> {
}
#[inline]
pub fn starts_with<'b>(self, base: impl Into<Url<'b>>) -> bool {
let base: Url = base.into();
pub fn starts_with(self, base: impl AsUrl) -> bool {
let base = base.as_url();
self.scheme.covariant(base.scheme) && self.loc.starts_with(base.loc)
}
#[inline]
pub fn ends_with<'b>(self, child: impl Into<Url<'b>>) -> bool {
let child: Url = child.into();
pub fn ends_with(self, child: impl AsUrl) -> bool {
let child = child.as_url();
self.scheme.covariant(child.scheme) && self.loc.ends_with(child.loc)
}
@ -160,8 +150,8 @@ impl<'a> Url<'a> {
pub fn os_str(self) -> Cow<'a, OsStr> { self.components().os_str() }
#[inline]
pub fn covariant(self, other: impl Into<Self>) -> bool {
let other = other.into();
pub fn covariant(self, other: impl AsUrl) -> bool {
let other = other.as_url();
self.scheme.covariant(other.scheme) && self.loc == other.loc
}

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 {

View file

@ -2,7 +2,7 @@ use anyhow::Result;
use hashbrown::HashSet;
use tokio::sync::mpsc;
use tracing::error;
use yazi_shared::url::{Url, UrlBuf};
use yazi_shared::url::{AsUrl, Url, UrlBuf};
use crate::{LINKED, WATCHED, backend};
@ -64,11 +64,11 @@ impl Backend {
todo.into_iter().for_each(|u| _ = out_tx.send(u));
}
fn watch<'a>(&mut self, url: impl Into<Url<'a>>) -> Result<()> {
if let Some(path) = url.into().as_path() { self.local.watch(path) } else { Ok(()) }
fn watch(&mut self, url: impl AsUrl) -> Result<()> {
if let Some(path) = url.as_url().as_path() { self.local.watch(path) } else { Ok(()) }
}
fn unwatch<'a>(&mut self, url: impl Into<Url<'a>>) -> Result<()> {
if let Some(path) = url.into().as_path() { self.local.unwatch(path) } else { Ok(()) }
fn unwatch(&mut self, url: impl AsUrl) -> Result<()> {
if let Some(path) = url.as_url().as_path() { self.local.unwatch(path) } else { Ok(()) }
}
}

View file

@ -20,10 +20,10 @@ impl DerefMut for Linked {
}
impl Linked {
pub fn from_dir<'a, 'b, T>(&'a self, url: T) -> Box<dyn Iterator<Item = &'a Path> + 'b>
pub fn from_dir<'a, 'b, U>(&'a self, url: U) -> Box<dyn Iterator<Item = &'a Path> + 'b>
where
'a: 'b,
T: Into<Url<'b>>,
U: Into<Url<'b>>,
{
let url: Url = url.into();
let Some(path) = url.as_path() else {

View file

@ -1,16 +1,14 @@
use std::path::Path;
use hashbrown::HashSet;
use yazi_shared::url::{Url, UrlBuf};
use yazi_shared::url::{AsUrl, UrlBuf};
#[derive(Default)]
pub struct Watched(HashSet<UrlBuf>);
impl Watched {
#[inline]
pub(crate) fn contains<'a>(&self, url: impl Into<Url<'a>>) -> bool {
self.0.contains(&url.into())
}
pub(crate) fn contains(&self, url: impl AsUrl) -> bool { self.0.contains(&url.as_url()) }
#[inline]
pub(crate) fn diff(&self, new: &HashSet<UrlBuf>) -> (Vec<UrlBuf>, Vec<UrlBuf>) {
@ -26,5 +24,5 @@ impl Watched {
}
#[inline]
pub(crate) fn remove<'a>(&mut self, url: impl Into<Url<'a>>) { self.0.remove(&url.into()); }
pub(crate) fn remove(&mut self, url: impl AsUrl) { self.0.remove(&url.as_url()); }
}