mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: new mimetype fetcher dedicated to remote files (#3268)
This commit is contained in:
parent
4e32044450
commit
4b08e8d899
32 changed files with 320 additions and 168 deletions
|
|
@ -5,7 +5,7 @@ use yazi_fs::{CWD, path::expand_url, provider::{DirReader, FileHolder}};
|
|||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt};
|
||||
use yazi_proxy::CmpProxy;
|
||||
use yazi_shared::{OsStrSplit, data::Data, natsort, url::{UrlBuf, UrlCow, UrnBuf}};
|
||||
use yazi_shared::{OsStrSplit, data::Data, natsort, scheme::SchemeLike, url::{UrlBuf, UrlCow, UrnBuf}};
|
||||
use yazi_vfs::provider;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
@ -70,7 +70,7 @@ impl Trigger {
|
|||
fn split_url(s: &str) -> Option<(UrlBuf, UrnBuf)> {
|
||||
let (scheme, path, ..) = UrlCow::parse(s.as_bytes()).ok()?;
|
||||
|
||||
if !scheme.is_virtual() && path.as_os_str() == "~" {
|
||||
if scheme.is_local() && path.as_os_str() == "~" {
|
||||
return None; // We don't autocomplete a `~`, but `~/`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
|||
use futures::{StreamExt, stream::FuturesUnordered};
|
||||
use hashbrown::HashSet;
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_fs::{File, FsUrl, provider::{Provider, local::Local}};
|
||||
use yazi_fs::{File, FsScheme, provider::{Provider, local::Local}};
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::mgr::{DownloadOpt, OpenOpt};
|
||||
use yazi_proxy::MgrProxy;
|
||||
|
|
@ -75,7 +75,7 @@ impl Actor for Download {
|
|||
|
||||
impl Download {
|
||||
async fn prepare(urls: &[UrlCow<'_>]) {
|
||||
let roots: HashSet<_> = urls.iter().filter_map(|u| u.cache_root()).collect();
|
||||
let roots: HashSet<_> = urls.iter().filter_map(|u| u.scheme().cache()).collect();
|
||||
for mut root in roots {
|
||||
root.push("%lock");
|
||||
Local.create_dir_all(root).await.ok();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{ops::Deref, time::{Duration, SystemTime}};
|
||||
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods};
|
||||
use yazi_fs::cha::{ChaKind, ChaMode};
|
||||
use yazi_fs::{FsHash128, cha::{ChaKind, ChaMode}};
|
||||
|
||||
#[derive(Clone, Copy, FromLua)]
|
||||
pub struct Cha(pub yazi_fs::cha::Cha);
|
||||
|
|
@ -76,6 +76,13 @@ impl UserData for Cha {
|
|||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("hash", |_, me, long: Option<bool>| {
|
||||
Ok(if long.unwrap_or(false) {
|
||||
format!("{:x}", me.hash_u128())
|
||||
} else {
|
||||
Err("Short hash not supported".into_lua_err())?
|
||||
})
|
||||
});
|
||||
methods.add_method("perm", |lua, _me, ()| {
|
||||
Ok(
|
||||
#[cfg(unix)]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{UserData, UserDataFields, Value};
|
||||
use yazi_fs::FsScheme;
|
||||
use yazi_shared::scheme::SchemeLike;
|
||||
|
||||
use crate::cached_field;
|
||||
use crate::{Url, cached_field};
|
||||
|
||||
pub struct Scheme {
|
||||
inner: yazi_shared::scheme::Scheme,
|
||||
|
||||
v_kind: Option<Value>,
|
||||
v_kind: Option<Value>,
|
||||
v_cache: Option<Value>,
|
||||
}
|
||||
|
||||
impl Deref for Scheme {
|
||||
|
|
@ -18,13 +21,14 @@ impl Deref for Scheme {
|
|||
|
||||
impl Scheme {
|
||||
pub fn new(scheme: &yazi_shared::scheme::Scheme) -> Self {
|
||||
Self { inner: scheme.clone(), v_kind: None }
|
||||
Self { inner: scheme.clone(), v_kind: None, v_cache: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Scheme {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, kind, |_, me| Ok(me.kind()));
|
||||
cached_field!(fields, cache, |_, me| Ok(me.cache().map(Url::new)));
|
||||
|
||||
fields.add_field_method_get("is_virtual", |_, me| Ok(me.is_virtual()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use yazi_shared::{IntoOsStr, url::{AsUrl, UrlCow, UrlLike}};
|
||||
use yazi_fs::{FsHash64, FsHash128};
|
||||
use yazi_shared::{IntoOsStr, scheme::SchemeLike, url::{AsUrl, UrlCow, UrlLike}};
|
||||
|
||||
use crate::{Scheme, Urn, cached_field, deprecate};
|
||||
|
||||
|
|
@ -128,6 +129,13 @@ impl UserData for Url {
|
|||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("hash", |_, me, long: Option<bool>| {
|
||||
Ok(if long.unwrap_or(false) {
|
||||
format!("{:x}", me.hash_u128())
|
||||
} else {
|
||||
format!("{:x}", me.hash_u64())
|
||||
})
|
||||
});
|
||||
methods.add_method("join", |_, me, other: Value| {
|
||||
Ok(Self::new(match other {
|
||||
Value::String(s) => me.join(s.as_bytes().into_os_str()?),
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ rules = [
|
|||
# Document
|
||||
{ mime = "application/{pdf,doc,rtf}", fg = "cyan" },
|
||||
# Virtual file system
|
||||
{ mime = "vfs/*", fg = "gray" },
|
||||
{ mime = "vfs/{absent,stale}", fg = "gray" },
|
||||
# Special file
|
||||
{ url = "*", is = "orphan", bg = "red" },
|
||||
{ url = "*", is = "exec" , fg = "green" },
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ rules = [
|
|||
# Document
|
||||
{ mime = "application/{pdf,doc,rtf}", fg = "cyan" },
|
||||
# Virtual file system
|
||||
{ mime = "vfs/*", fg = "darkgray" },
|
||||
{ mime = "vfs/{absent,stale}", fg = "darkgray" },
|
||||
# Special file
|
||||
{ url = "*", is = "orphan", bg = "red" },
|
||||
{ url = "*", is = "exec" , fg = "green" },
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ rules = [
|
|||
# Empty file
|
||||
{ mime = "inode/empty", use = [ "edit", "reveal" ] },
|
||||
# Virtual file system
|
||||
{ mime = "vfs/*", use = "download" },
|
||||
{ mime = "vfs/{absent,stale}", use = "download" },
|
||||
# Fallback
|
||||
{ url = "*", use = [ "open", "reveal" ] },
|
||||
]
|
||||
|
|
@ -97,8 +97,9 @@ suppress_preload = false
|
|||
[plugin]
|
||||
fetchers = [
|
||||
# Mimetype
|
||||
{ id = "mime", url = "*/", run = "mime.dir", prio = "high" },
|
||||
{ id = "mime", url = "*", run = "mime.file", prio = "high" },
|
||||
{ id = "mime", url = "*/", run = "mime.dir", prio = "high" },
|
||||
{ id = "mime", url = "local://*", run = "mime.local", prio = "high" },
|
||||
{ id = "mime", url = "remote://*", run = "mime.remote", prio = "high" },
|
||||
]
|
||||
spotters = [
|
||||
{ url = "*/", run = "folder" },
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::str::FromStr;
|
||||
use std::{fmt::Debug, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{Result, bail};
|
||||
use globset::GlobBuilder;
|
||||
use serde::Deserialize;
|
||||
use yazi_shared::{scheme::{SchemeCow, SchemeRef}, url::AsUrl};
|
||||
use yazi_shared::{scheme::SchemeRef, url::AsUrl};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Deserialize)]
|
||||
#[serde(try_from = "String")]
|
||||
pub struct Pattern {
|
||||
inner: globset::GlobMatcher,
|
||||
|
|
@ -16,16 +16,27 @@ pub struct Pattern {
|
|||
sep_lit: bool,
|
||||
}
|
||||
|
||||
impl Debug for Pattern {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Pattern")
|
||||
.field("regex", &self.inner.glob().regex())
|
||||
.field("scheme", &self.scheme)
|
||||
.field("is_dir", &self.is_dir)
|
||||
.field("is_star", &self.is_star)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
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;
|
||||
} else if self.is_star {
|
||||
return true;
|
||||
} else if !self.scheme.matches(url.scheme) {
|
||||
return false;
|
||||
} else if self.is_star {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
|
@ -95,26 +106,59 @@ impl TryFrom<String> for Pattern {
|
|||
}
|
||||
|
||||
// --- Scheme
|
||||
#[derive(Debug)]
|
||||
struct PatternScheme(Option<&'static str>);
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum PatternScheme {
|
||||
Any,
|
||||
Local,
|
||||
Remote,
|
||||
Virtual,
|
||||
|
||||
Regular,
|
||||
Search,
|
||||
Archive,
|
||||
Sftp,
|
||||
}
|
||||
|
||||
impl PatternScheme {
|
||||
fn parse(s: &str) -> Result<(Self, usize)> {
|
||||
let mut me = Self(None);
|
||||
let Some((protocol, _)) = s.split_once("://") else {
|
||||
return Ok((me, 0));
|
||||
return Ok((Self::Any, 0));
|
||||
};
|
||||
|
||||
if protocol != "*" {
|
||||
me.0 = Some(SchemeCow::parse_kind(protocol.as_bytes())?);
|
||||
}
|
||||
let scheme = match protocol {
|
||||
"*" => Self::Any,
|
||||
"local" => Self::Local,
|
||||
"remote" => Self::Remote,
|
||||
"virtual" => Self::Virtual,
|
||||
|
||||
Ok((me, protocol.len() + 3))
|
||||
"regular" => Self::Regular,
|
||||
"search" => Self::Search,
|
||||
"archive" => Self::Archive,
|
||||
"sftp" => Self::Sftp,
|
||||
|
||||
"" => bail!("Invalid URL pattern: protocol is empty"),
|
||||
_ => bail!("Unknown protocol in URL pattern: {protocol}"),
|
||||
};
|
||||
|
||||
Ok((scheme, protocol.len() + 3))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn matches<'a>(&self, scheme: impl Into<SchemeRef<'a>>) -> bool {
|
||||
self.0.is_none_or(|s| s == scheme.into().kind())
|
||||
fn matches(self, scheme: SchemeRef) -> bool {
|
||||
use SchemeRef as S;
|
||||
match (self, scheme) {
|
||||
(Self::Any, _) => true,
|
||||
(Self::Local, s) => s.is_local(),
|
||||
(Self::Remote, s) => s.is_remote(),
|
||||
(Self::Virtual, s) => s.is_virtual(),
|
||||
|
||||
(Self::Regular, S::Regular) => true,
|
||||
(Self::Search, S::Search(_)) => true,
|
||||
(Self::Archive, S::Archive(_)) => true,
|
||||
(Self::Sftp, S::Sftp(_)) => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,9 +30,8 @@ impl FsHash128 for Cha {
|
|||
self.mode.bits().hash(&mut h);
|
||||
self.len.hash(&mut h);
|
||||
|
||||
self.mtime_dur().ok().map(|d| d.as_nanos()).hash(&mut h);
|
||||
self.btime_dur().ok().map(|d| d.as_nanos()).hash(&mut h);
|
||||
self.ctime_dur().ok().map(|d| d.as_nanos()).hash(&mut h);
|
||||
self.mtime_dur().ok().map(|d| d.as_nanos()).hash(&mut h);
|
||||
|
||||
h.finish_128()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
yazi_macro::mod_pub!(cha error mounts path provider);
|
||||
|
||||
yazi_macro::mod_flat!(cwd file files filter fns hash op sorter sorting splatter stage url xdg);
|
||||
yazi_macro::mod_flat!(cwd file files filter fns hash op scheme sorter sorting splatter stage url xdg);
|
||||
|
||||
pub fn init() {
|
||||
CWD.init(<_>::default());
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ impl FileBuilder for Gate {
|
|||
}
|
||||
|
||||
async fn new(scheme: SchemeRef<'_>) -> io::Result<Self> {
|
||||
if scheme.is_virtual() {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "Not a local filesystem"))?
|
||||
} else {
|
||||
if scheme.is_local() {
|
||||
Ok(Self::default())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "Not a local filesystem"))?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ impl Provider for Local {
|
|||
U: AsUrl,
|
||||
{
|
||||
let url = url.as_url();
|
||||
if url.scheme.is_virtual() {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "Not a local URL"))
|
||||
} else {
|
||||
if url.scheme.is_local() {
|
||||
Ok(absolute_url(url))
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "Not a local URL"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
27
yazi-fs/src/scheme.rs
Normal file
27
yazi-fs/src/scheme.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use yazi_shared::scheme::{AsScheme, Scheme, SchemeRef};
|
||||
|
||||
use crate::Xdg;
|
||||
|
||||
pub trait FsScheme {
|
||||
fn cache(&self) -> Option<PathBuf>;
|
||||
}
|
||||
|
||||
impl FsScheme for SchemeRef<'_> {
|
||||
fn cache(&self) -> Option<PathBuf> {
|
||||
match self {
|
||||
SchemeRef::Regular | SchemeRef::Search(_) => None,
|
||||
SchemeRef::Archive(name) => {
|
||||
Some(Xdg::cache_dir().join(format!("archive-{}", yazi_shared::url::Encode::domain(name))))
|
||||
}
|
||||
SchemeRef::Sftp(name) => {
|
||||
Some(Xdg::cache_dir().join(format!("sftp-{}", yazi_shared::url::Encode::domain(name))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FsScheme for Scheme {
|
||||
fn cache(&self) -> Option<PathBuf> { self.as_scheme().cache() }
|
||||
}
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
||||
|
||||
use yazi_shared::{loc::Loc, scheme::SchemeRef, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
||||
use yazi_shared::{loc::Loc, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
||||
|
||||
use crate::{FsHash128, Xdg, path::PercentEncoding};
|
||||
use crate::{FsHash128, FsScheme, path::PercentEncoding};
|
||||
|
||||
pub trait FsUrl<'a> {
|
||||
fn cache(&self) -> Option<PathBuf>;
|
||||
|
||||
fn cache_lock(&self) -> Option<PathBuf>;
|
||||
|
||||
fn cache_root(&self) -> Option<PathBuf>;
|
||||
|
||||
fn unified_path(self) -> Cow<'a, Path>;
|
||||
|
||||
fn unified_path_str(self) -> Cow<'a, OsStr>
|
||||
|
|
@ -37,29 +35,17 @@ impl<'a> FsUrl<'a> for Url<'a> {
|
|||
path
|
||||
}
|
||||
|
||||
self.cache_root().map(|root| with_loc(self.loc, root))
|
||||
self.scheme.cache().map(|root| with_loc(self.loc, root))
|
||||
}
|
||||
|
||||
fn cache_lock(&self) -> Option<PathBuf> {
|
||||
self.cache_root().map(|mut root| {
|
||||
self.scheme.cache().map(|mut root| {
|
||||
root.push("%lock");
|
||||
root.push(format!("{:x}", self.hash_u128()));
|
||||
root
|
||||
})
|
||||
}
|
||||
|
||||
fn cache_root(&self) -> Option<PathBuf> {
|
||||
match self.scheme {
|
||||
SchemeRef::Regular | SchemeRef::Search(_) => None,
|
||||
SchemeRef::Archive(name) => {
|
||||
Some(Xdg::cache_dir().join(format!("archive-{}", yazi_shared::url::Encode::domain(name))))
|
||||
}
|
||||
SchemeRef::Sftp(name) => {
|
||||
Some(Xdg::cache_dir().join(format!("sftp-{}", yazi_shared::url::Encode::domain(name))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unified_path(self) -> Cow<'a, Path> {
|
||||
self.cache().map(Cow::Owned).unwrap_or_else(|| Cow::Borrowed(self.loc.as_path()))
|
||||
}
|
||||
|
|
@ -70,8 +56,6 @@ impl FsUrl<'_> for UrlBuf {
|
|||
|
||||
fn cache_lock(&self) -> Option<PathBuf> { self.as_url().cache_lock() }
|
||||
|
||||
fn cache_root(&self) -> Option<PathBuf> { self.as_url().cache_root() }
|
||||
|
||||
fn unified_path(self) -> Cow<'static, Path> {
|
||||
self.cache().unwrap_or_else(|| self.loc.into_path()).into()
|
||||
}
|
||||
|
|
@ -82,8 +66,6 @@ impl<'a> FsUrl<'a> for UrlCow<'a> {
|
|||
|
||||
fn cache_lock(&self) -> Option<PathBuf> { self.as_url().cache_lock() }
|
||||
|
||||
fn cache_root(&self) -> Option<PathBuf> { self.as_url().cache_root() }
|
||||
|
||||
fn unified_path(self) -> Cow<'a, Path> {
|
||||
match (self.cache(), self) {
|
||||
(None, UrlCow::Borrowed { loc, .. }) => loc.as_path().into(),
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@ function M:spot_base(job)
|
|||
for i, v in ipairs(fetchers) do
|
||||
fetchers[i] = v.cmd
|
||||
end
|
||||
fetchers = #fetchers ~= 0 and fetchers or { "-" }
|
||||
|
||||
for i, v in ipairs(preloaders) do
|
||||
preloaders[i] = v.cmd
|
||||
end
|
||||
preloaders = #preloaders ~= 0 and preloaders or { "-" }
|
||||
|
||||
return {
|
||||
ui.Row({ "Base" }):style(ui.Style():fg("green")),
|
||||
|
|
@ -54,8 +57,8 @@ function M:spot_base(job)
|
|||
ui.Row({ "Plugins" }):style(ui.Style():fg("green")),
|
||||
ui.Row { " Spotter:", spotter and spotter.cmd or "-" },
|
||||
ui.Row { " Previewer:", previewer and previewer.cmd or "-" },
|
||||
ui.Row { " Fetchers:", #fetchers ~= 0 and fetchers or "-" },
|
||||
ui.Row { " Preloaders:", #preloaders ~= 0 and preloaders or "-" },
|
||||
ui.Row({ " Fetchers:", fetchers }):height(#fetchers),
|
||||
ui.Row({ " Preloaders:", preloaders }):height(#preloaders),
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -15,20 +15,11 @@ local function match_mimetype(line)
|
|||
end
|
||||
end
|
||||
|
||||
local function miss_cache(cache, line)
|
||||
if line:match("^cannot open `.+' %(No such file or directory%)%s+$") then
|
||||
return true
|
||||
else
|
||||
local _, err = fs.cha(Url(cache))
|
||||
return err and err.code == 2
|
||||
end
|
||||
end
|
||||
|
||||
function M:fetch(job)
|
||||
local paths, origins = {}, {}
|
||||
local urls, paths = {}, {}
|
||||
for i, file in ipairs(job.files) do
|
||||
if file.cache then
|
||||
paths[i], origins[i] = tostring(file.cache), tostring(file.url)
|
||||
urls[i], paths[i] = tostring(file.url), tostring(file.cache)
|
||||
else
|
||||
paths[i] = tostring(file.url)
|
||||
end
|
||||
|
|
@ -63,14 +54,9 @@ function M:fetch(job)
|
|||
|
||||
match, ignore = match_mimetype(line)
|
||||
if match then
|
||||
updates[origins[i] or paths[i]], state[i], i = match, true, i + 1
|
||||
updates[urls[i] or paths[i]], state[i], i = match, true, i + 1
|
||||
flush(false)
|
||||
elseif ignore then
|
||||
goto continue
|
||||
elseif origins[i] and miss_cache(paths[i], line) then
|
||||
updates[origins[i]], state[i], i = "vfs/todo", true, i + 1
|
||||
flush(false)
|
||||
else
|
||||
elseif not ignore then
|
||||
state[i], i = false, i + 1
|
||||
end
|
||||
::continue::
|
||||
59
yazi-plugin/preset/plugins/mime-remote.lua
Normal file
59
yazi-plugin/preset/plugins/mime-remote.lua
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
local M = {}
|
||||
|
||||
local function stale_cache(file)
|
||||
local url = file.url
|
||||
local lock = url.scheme.cache:join(string.format("%%lock/%s", url:hash(true)))
|
||||
|
||||
local f = io.open(tostring(lock), "r")
|
||||
if not f then
|
||||
return true
|
||||
end
|
||||
|
||||
local hash = f:read(32)
|
||||
f:close()
|
||||
return hash ~= file.cha:hash(true)
|
||||
end
|
||||
|
||||
function M:fetch(job)
|
||||
local updates, unknown, state = {}, {}, {}
|
||||
for i, file in ipairs(job.files) do
|
||||
if not file.cache then
|
||||
unknown[#unknown + 1] = file
|
||||
elseif not fs.cha(file.cache) then
|
||||
updates[file.url], state[i] = "vfs/absent", true
|
||||
elseif stale_cache(file) then
|
||||
updates[file.url], state[i] = "vfs/stale", true
|
||||
else
|
||||
unknown[#unknown + 1] = file
|
||||
end
|
||||
end
|
||||
|
||||
if next(updates) then
|
||||
ya.emit("update_mimes", { updates = updates })
|
||||
end
|
||||
|
||||
if #unknown == 0 then
|
||||
return state
|
||||
else
|
||||
return self.fallback_local(job, unknown, state)
|
||||
end
|
||||
end
|
||||
|
||||
function M.fallback_local(job, unknown, state)
|
||||
local indices = {}
|
||||
for i, f in ipairs(job.files) do
|
||||
indices[f:hash()] = i
|
||||
end
|
||||
|
||||
local result = require("mime.local"):fetch(ya.dict_merge(job, { files = unknown }))
|
||||
for i, f in ipairs(unknown) do
|
||||
if type(result) == "table" then
|
||||
state[indices[f:hash()]] = result[i]
|
||||
else
|
||||
state[indices[f:hash()]] = result
|
||||
end
|
||||
end
|
||||
return state
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
local function fetch(_, job)
|
||||
ya.notify {
|
||||
title = "Deprecated plugin",
|
||||
content = "The `mime` fetcher is deprecated, use `mime.file` instead in your `yazi.toml`\n\nSee https://github.com/sxyazi/yazi/pull/3222 for more details.",
|
||||
content = "The `mime` fetcher is deprecated, use `mime.local` instead in your `yazi.toml`\n\nSee https://github.com/sxyazi/yazi/pull/3222 for more details.",
|
||||
timeout = 15,
|
||||
level = "warn",
|
||||
}
|
||||
|
||||
return require("mime.file"):fetch(job)
|
||||
return require("mime.local"):fetch(job)
|
||||
end
|
||||
|
||||
return { fetch = fetch }
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ impl Default for Loader {
|
|||
("magick".to_owned(), preset!("plugins/magick").into()),
|
||||
("mime".to_owned(), preset!("plugins/mime").into()), // TODO: remove this
|
||||
("mime.dir".to_owned(), preset!("plugins/mime-dir").into()),
|
||||
("mime.file".to_owned(), preset!("plugins/mime-file").into()),
|
||||
("mime.local".to_owned(), preset!("plugins/mime-local").into()),
|
||||
("mime.remote".to_owned(), preset!("plugins/mime-remote").into()),
|
||||
("noop".to_owned(), preset!("plugins/noop").into()),
|
||||
("pdf".to_owned(), preset!("plugins/pdf").into()),
|
||||
("session".to_owned(), preset!("plugins/session").into()),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use tracing::warn;
|
|||
use yazi_config::YAZI;
|
||||
use yazi_fs::{Cwd, FsHash128, FsUrl, cha::Cha, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{DirReader, FileHolder, Provider, local::Local}};
|
||||
use yazi_macro::ok_or_not_found;
|
||||
use yazi_shared::{timestamp_us, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_shared::{scheme::SchemeLike, timestamp_us, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_vfs::{VfsCha, copy_with_progress, maybe_exists, provider::{self, DirEntry}, unique_name};
|
||||
|
||||
use super::{FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use yazi_config::{YAZI, plugin::{Fetcher, Preloader}};
|
|||
use yazi_dds::Pump;
|
||||
use yazi_parser::{app::PluginOpt, tasks::ProcessOpenOpt};
|
||||
use yazi_proxy::TasksProxy;
|
||||
use yazi_shared::{Id, Throttle, url::{UrlBuf, UrlLike}};
|
||||
use yazi_shared::{Id, Throttle, scheme::SchemeLike, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::{must_be_dir, provider, unique_name};
|
||||
|
||||
use super::{Ongoing, TaskOp};
|
||||
|
|
@ -215,7 +215,7 @@ impl Scheduler {
|
|||
ongoing.hooks.add_sync(id, move |canceled| _ = tx.send(canceled));
|
||||
}
|
||||
|
||||
if !url.scheme.is_virtual() {
|
||||
if !url.scheme.is_remote() {
|
||||
return self.ops.out(id, FileOutDownload::Fail("Cannot download non-remote file".to_owned()));
|
||||
};
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ impl Scheduler {
|
|||
let mut ongoing = self.ongoing.lock();
|
||||
let id = ongoing.add::<FileProgUpload>(format!("Upload {}", url.display()));
|
||||
|
||||
if !url.scheme.is_virtual() {
|
||||
if !url.scheme.is_remote() {
|
||||
return self.ops.out(id, FileOutUpload::Fail("Cannot upload non-remote file".to_owned()));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, ops::Not, path::Path};
|
|||
use anyhow::{Result, bail, ensure};
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use crate::{BytesExt, pool::InternStr, scheme::{Scheme, SchemeRef}};
|
||||
use crate::{BytesExt, pool::InternStr, scheme::{AsScheme, Scheme, SchemeRef}};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SchemeCow<'a> {
|
||||
|
|
@ -15,18 +15,21 @@ impl Default for SchemeCow<'_> {
|
|||
fn default() -> Self { Self::Borrowed(SchemeRef::Regular) }
|
||||
}
|
||||
|
||||
impl From<Scheme> for SchemeCow<'_> {
|
||||
fn from(value: Scheme) -> Self { Self::Owned(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Scheme> for SchemeCow<'a> {
|
||||
fn from(value: &'a Scheme) -> Self { Self::Borrowed(value.as_ref()) }
|
||||
}
|
||||
|
||||
impl<'a> From<SchemeRef<'a>> for SchemeCow<'a> {
|
||||
fn from(value: SchemeRef<'a>) -> Self { Self::Borrowed(value) }
|
||||
}
|
||||
|
||||
impl<'a, T> From<&'a T> for SchemeCow<'a>
|
||||
where
|
||||
T: AsScheme + ?Sized,
|
||||
{
|
||||
fn from(value: &'a T) -> Self { Self::Borrowed(value.as_scheme()) }
|
||||
}
|
||||
|
||||
impl From<Scheme> for SchemeCow<'_> {
|
||||
fn from(value: Scheme) -> Self { Self::Owned(value) }
|
||||
}
|
||||
|
||||
impl From<SchemeCow<'_>> for Scheme {
|
||||
fn from(value: SchemeCow<'_>) -> Self {
|
||||
match value {
|
||||
|
|
@ -58,14 +61,6 @@ impl<'a> SchemeCow<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_ref(&self) -> SchemeRef<'_> {
|
||||
match self {
|
||||
Self::Borrowed(s) => *s,
|
||||
Self::Owned(s) => s.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse(
|
||||
bytes: &'a [u8],
|
||||
skip: &mut usize,
|
||||
|
|
@ -103,17 +98,6 @@ impl<'a> SchemeCow<'a> {
|
|||
Ok((scheme, tilde, uri, urn))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn parse_kind(bytes: &[u8]) -> Result<&'static str> {
|
||||
match bytes {
|
||||
b"regular" => Ok("regular"),
|
||||
b"search" => Ok("search"),
|
||||
b"archive" => Ok("archive"),
|
||||
b"sftp" => Ok("sftp"),
|
||||
_ => bail!("Could not parse protocol from URL: {}", String::from_utf8_lossy(bytes)),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_param(
|
||||
bytes: &'a [u8],
|
||||
skip: &mut usize,
|
||||
|
|
@ -154,7 +138,7 @@ impl<'a> SchemeCow<'a> {
|
|||
urn: Option<usize>,
|
||||
path: &Path,
|
||||
) -> Result<Option<(usize, usize)>> {
|
||||
Ok(match self.as_ref() {
|
||||
Ok(match self.as_scheme() {
|
||||
SchemeRef::Regular => {
|
||||
ensure!(uri.is_none() && urn.is_none(), "Regular scheme cannot have ports");
|
||||
None
|
||||
|
|
@ -175,9 +159,6 @@ impl<'a> SchemeCow<'a> {
|
|||
}
|
||||
|
||||
impl SchemeCow<'_> {
|
||||
#[inline]
|
||||
pub fn is_virtual(&self) -> bool { self.as_ref().is_virtual() }
|
||||
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> Scheme { self.into() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(cow r#ref scheme);
|
||||
yazi_macro::mod_flat!(cow r#ref scheme traits);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{pool::InternStr, scheme::Scheme};
|
||||
use crate::{pool::InternStr, scheme::{AsScheme, Scheme}};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub enum SchemeRef<'a> {
|
||||
|
|
@ -12,17 +12,6 @@ pub enum SchemeRef<'a> {
|
|||
Sftp(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Scheme> for SchemeRef<'a> {
|
||||
fn from(value: &'a Scheme) -> Self {
|
||||
match value {
|
||||
Scheme::Regular => Self::Regular,
|
||||
Scheme::Search(d) => Self::Search(d),
|
||||
Scheme::Archive(d) => Self::Archive(d),
|
||||
Scheme::Sftp(d) => Self::Sftp(d),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SchemeRef<'_>> for Scheme {
|
||||
fn from(value: SchemeRef) -> Self {
|
||||
match value {
|
||||
|
|
@ -54,13 +43,29 @@ impl<'a> SchemeRef<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(self, other: impl Into<Self>) -> bool {
|
||||
let other = other.into();
|
||||
pub fn covariant(self, other: impl AsScheme) -> bool {
|
||||
let other = other.as_scheme();
|
||||
if self.is_virtual() || other.is_virtual() { self == other } else { true }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_virtual(&self) -> bool {
|
||||
pub fn is_local(self) -> bool {
|
||||
match self {
|
||||
Self::Regular | Self::Search(_) => true,
|
||||
Self::Archive(_) | Self::Sftp(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_remote(self) -> bool {
|
||||
match self {
|
||||
Self::Regular | Self::Search(_) | Self::Archive(_) => false,
|
||||
Self::Sftp(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_virtual(self) -> bool {
|
||||
match self {
|
||||
Self::Regular | Self::Search(_) => false,
|
||||
Self::Archive(_) | Self::Sftp(_) => true,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::{pool::Symbol, scheme::SchemeRef};
|
||||
use crate::{pool::Symbol, scheme::{AsScheme, SchemeRef}};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub enum Scheme {
|
||||
|
|
@ -15,26 +15,9 @@ pub enum Scheme {
|
|||
}
|
||||
|
||||
impl Hash for Scheme {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.as_ref().hash(state); }
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.as_scheme().hash(state); }
|
||||
}
|
||||
|
||||
impl PartialEq<SchemeRef<'_>> for Scheme {
|
||||
fn eq(&self, other: &SchemeRef<'_>) -> bool { self.as_ref() == *other }
|
||||
}
|
||||
|
||||
impl Scheme {
|
||||
#[inline]
|
||||
pub fn as_ref(&self) -> SchemeRef<'_> { self.into() }
|
||||
|
||||
#[inline]
|
||||
pub fn kind(&self) -> &'static str { self.as_ref().kind() }
|
||||
|
||||
#[inline]
|
||||
pub fn domain(&self) -> Option<&str> { self.as_ref().domain() }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: &Self) -> bool { self.as_ref().covariant(other) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_virtual(&self) -> bool { self.as_ref().is_virtual() }
|
||||
fn eq(&self, other: &SchemeRef<'_>) -> bool { self.as_scheme() == *other }
|
||||
}
|
||||
|
|
|
|||
63
yazi-shared/src/scheme/traits.rs
Normal file
63
yazi-shared/src/scheme/traits.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
use crate::scheme::{Scheme, SchemeCow, SchemeRef};
|
||||
|
||||
pub trait AsScheme {
|
||||
fn as_scheme(&self) -> SchemeRef<'_>;
|
||||
}
|
||||
|
||||
impl AsScheme for SchemeRef<'_> {
|
||||
#[inline]
|
||||
fn as_scheme(&self) -> SchemeRef<'_> { *self }
|
||||
}
|
||||
|
||||
impl AsScheme for Scheme {
|
||||
#[inline]
|
||||
fn as_scheme(&self) -> SchemeRef<'_> {
|
||||
match self {
|
||||
Scheme::Regular => SchemeRef::Regular,
|
||||
Scheme::Search(d) => SchemeRef::Search(d),
|
||||
Scheme::Archive(d) => SchemeRef::Archive(d),
|
||||
Scheme::Sftp(d) => SchemeRef::Sftp(d),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsScheme for &Scheme {
|
||||
#[inline]
|
||||
fn as_scheme(&self) -> SchemeRef<'_> { (**self).as_scheme() }
|
||||
}
|
||||
|
||||
impl AsScheme for SchemeCow<'_> {
|
||||
#[inline]
|
||||
fn as_scheme(&self) -> SchemeRef<'_> {
|
||||
match self {
|
||||
SchemeCow::Borrowed(s) => *s,
|
||||
SchemeCow::Owned(s) => s.as_scheme(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsScheme for &SchemeCow<'_> {
|
||||
#[inline]
|
||||
fn as_scheme(&self) -> SchemeRef<'_> { (**self).as_scheme() }
|
||||
}
|
||||
|
||||
// --- SchemeLike
|
||||
pub trait SchemeLike
|
||||
where
|
||||
Self: AsScheme + Sized,
|
||||
{
|
||||
fn kind(&self) -> &'static str { self.as_scheme().kind() }
|
||||
|
||||
fn domain(&self) -> Option<&str> { self.as_scheme().domain() }
|
||||
|
||||
fn covariant(&self, other: impl AsScheme) -> bool { self.as_scheme().covariant(other) }
|
||||
|
||||
fn is_local(&self) -> bool { self.as_scheme().is_local() }
|
||||
|
||||
fn is_remote(&self) -> bool { self.as_scheme().is_remote() }
|
||||
|
||||
fn is_virtual(&self) -> bool { self.as_scheme().is_virtual() }
|
||||
}
|
||||
|
||||
impl SchemeLike for Scheme {}
|
||||
impl SchemeLike for SchemeCow<'_> {}
|
||||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, path::{Path, PathBuf
|
|||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{loc::LocBuf, pool::Pool, scheme::Scheme, url::{AsUrl, Encode, EncodeTilded, Url, UrlCow}};
|
||||
use crate::{loc::LocBuf, pool::Pool, scheme::{Scheme, SchemeLike}, url::{AsUrl, Encode, EncodeTilded, Url, UrlCow}};
|
||||
|
||||
#[derive(Clone, Default, Eq, Hash, PartialEq)]
|
||||
pub struct UrlBuf {
|
||||
|
|
@ -91,7 +91,7 @@ impl UrlBuf {
|
|||
|
||||
#[inline]
|
||||
pub fn into_path(self) -> Option<PathBuf> {
|
||||
Some(self.loc.into_path()).filter(|_| !self.scheme.is_virtual())
|
||||
Some(self.loc.into_path()).filter(|_| self.scheme.is_local())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl<'a> From<Url<'a>> for Components<'a> {
|
|||
impl<'a> Components<'a> {
|
||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||
let path = self.inner.as_path();
|
||||
if !self.url.scheme.is_virtual() || self.scheme_yielded {
|
||||
if self.url.scheme.is_local() || self.scheme_yielded {
|
||||
return path.as_os_str().into();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, path::{Path, PathBuf}};
|
|||
use anyhow::Result;
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use crate::{IntoOsStr, loc::{Loc, LocBuf}, scheme::{SchemeCow, SchemeRef}, url::{AsUrl, Url, UrlBuf}};
|
||||
use crate::{IntoOsStr, loc::{Loc, LocBuf}, scheme::{AsScheme, SchemeCow, SchemeRef}, url::{AsUrl, Url, UrlBuf}};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum UrlCow<'a> {
|
||||
|
|
@ -109,8 +109,8 @@ impl<'a> UrlCow<'a> {
|
|||
#[inline]
|
||||
pub fn scheme(&self) -> SchemeRef<'_> {
|
||||
match self {
|
||||
UrlCow::Borrowed { scheme, .. } => scheme.as_ref(),
|
||||
UrlCow::Owned { scheme, .. } => scheme.as_ref(),
|
||||
UrlCow::Borrowed { scheme, .. } => scheme.as_scheme(),
|
||||
UrlCow::Owned { scheme, .. } => scheme.as_scheme(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
||||
|
||||
use crate::{scheme::SchemeRef, url::{Components, Display, Uri, Url, UrlBuf, UrlCow, Urn}};
|
||||
use crate::{scheme::{AsScheme, SchemeRef}, url::{Components, Display, Uri, Url, UrlBuf, UrlCow, Urn}};
|
||||
|
||||
// --- AsUrl
|
||||
pub trait AsUrl {
|
||||
|
|
@ -34,7 +34,7 @@ impl AsUrl for Url<'_> {
|
|||
|
||||
impl AsUrl for UrlBuf {
|
||||
#[inline]
|
||||
fn as_url(&self) -> Url<'_> { Url { loc: self.loc.as_loc(), scheme: self.scheme.as_ref() } }
|
||||
fn as_url(&self) -> Url<'_> { Url { loc: self.loc.as_loc(), scheme: self.scheme.as_scheme() } }
|
||||
}
|
||||
|
||||
impl AsUrl for &UrlBuf {
|
||||
|
|
@ -51,8 +51,8 @@ impl AsUrl for UrlCow<'_> {
|
|||
#[inline]
|
||||
fn as_url(&self) -> Url<'_> {
|
||||
match self {
|
||||
UrlCow::Borrowed { loc, scheme } => Url { loc: *loc, scheme: scheme.as_ref() },
|
||||
UrlCow::Owned { loc, scheme } => Url { loc: loc.as_loc(), scheme: scheme.as_ref() },
|
||||
UrlCow::Borrowed { loc, scheme } => Url { loc: *loc, scheme: scheme.as_scheme() },
|
||||
UrlCow::Owned { loc, scheme } => Url { loc: loc.as_loc(), scheme: scheme.as_scheme() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -122,6 +122,5 @@ where
|
|||
fn urn(&self) -> &Urn { self.as_url().urn() }
|
||||
}
|
||||
|
||||
impl UrlLike for Url<'_> {}
|
||||
impl UrlLike for UrlBuf {}
|
||||
impl UrlLike for UrlCow<'_> {}
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ impl<'a> Url<'a> {
|
|||
|
||||
#[inline]
|
||||
pub fn as_path(self) -> Option<&'a Path> {
|
||||
Some(self.loc.as_path()).filter(|_| !self.scheme.is_virtual())
|
||||
Some(self.loc.as_path()).filter(|_| self.scheme.is_local())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue