mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: WTF-8 validator (#3374)
This commit is contained in:
parent
329c80cbee
commit
3561867b99
26 changed files with 308 additions and 166 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
|
@ -3876,21 +3876,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-appender"
|
name = "tracing-appender"
|
||||||
version = "0.2.3"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
|
checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.17",
|
||||||
"time",
|
"time",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.30"
|
version = "0.1.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -3899,9 +3899,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.34"
|
version = "0.1.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"valuable",
|
"valuable",
|
||||||
|
|
@ -4615,9 +4615,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.13"
|
version = "0.7.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winsafe"
|
name = "winsafe"
|
||||||
|
|
@ -4732,7 +4732,6 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
|
||||||
"vergen-gitcl",
|
"vergen-gitcl",
|
||||||
"yazi-adapter",
|
"yazi-adapter",
|
||||||
"yazi-config",
|
"yazi-config",
|
||||||
|
|
@ -4933,6 +4932,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"trash",
|
"trash",
|
||||||
|
"typed-path",
|
||||||
"uzers",
|
"uzers",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
"yazi-ffi",
|
"yazi-ffi",
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@ impl Refresh {
|
||||||
|
|
||||||
// TODO: performance improvement
|
// TODO: performance improvement
|
||||||
fn trigger_dirs(folders: &[&Folder]) {
|
fn trigger_dirs(folders: &[&Folder]) {
|
||||||
async fn go(cwd: UrlBuf, cha: Cha) {
|
async fn go(dir: UrlBuf, cha: Cha) {
|
||||||
let Some(cha) = Files::assert_stale(&cwd, cha).await else { return };
|
let Some(cha) = Files::assert_stale(&dir, cha).await else { return };
|
||||||
|
|
||||||
match Files::from_dir_bulk(&cwd).await {
|
match Files::from_dir_bulk(&dir).await {
|
||||||
Ok(files) => FilesOp::Full(cwd, files, cha).emit(),
|
Ok(files) => FilesOp::Full(dir, files, cha).emit(),
|
||||||
Err(e) => FilesOp::issue_error(&cwd, e).await,
|
Err(e) => FilesOp::issue_error(&dir, e).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ clap = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
serde = { workspace = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
yazi-shared = { path = "../yazi-shared", version = "25.9.15" }
|
yazi-shared = { path = "../yazi-shared", version = "25.9.15" }
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ tokio-stream = { workspace = true }
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-appender = "0.2.3"
|
tracing-appender = "0.2.4"
|
||||||
tracing-subscriber = { version = "0.3.20", features = [ "env-filter" ] }
|
tracing-subscriber = { version = "0.3.20", features = [ "env-filter" ] }
|
||||||
|
|
||||||
[target."cfg(unix)".dependencies]
|
[target."cfg(unix)".dependencies]
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ scopeguard = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
typed-path = { workspace = true }
|
||||||
|
|
||||||
[target."cfg(unix)".dependencies]
|
[target."cfg(unix)".dependencies]
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{borrow::Cow, path::PathBuf};
|
use std::{borrow::Cow, path::PathBuf};
|
||||||
|
|
||||||
use yazi_shared::{FromWtf8Vec, loc::LocBuf, path::{PathBufDyn, PathCow, PathDyn, PathKind, PathLike}, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
|
use yazi_shared::{loc::LocBuf, path::{PathBufDyn, PathCow, PathDyn, PathKind, PathLike}, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}, wtf8::FromWtf8Vec};
|
||||||
|
|
||||||
use crate::{CWD, path::clean_url};
|
use crate::{CWD, path::clean_url};
|
||||||
|
|
||||||
|
|
@ -25,21 +25,25 @@ fn expand_url_impl<'a>(url: Url<'a>) -> UrlCow<'a> {
|
||||||
let mut path = PathBufDyn::with_capacity(url.kind(), n_base.len() + n_rest.len() + n_urn.len());
|
let mut path = PathBufDyn::with_capacity(url.kind(), n_base.len() + n_rest.len() + n_urn.len());
|
||||||
path.try_extend([n_base, n_rest, n_urn]).expect("extend original parts should not fail");
|
path.try_extend([n_base, n_rest, n_urn]).expect("extend original parts should not fail");
|
||||||
|
|
||||||
let loc = LocBuf::<PathBuf>::with(
|
let uri = (uri_count + rest_diff + urn_diff) as usize;
|
||||||
path.into_os().expect("Failed to convert PathBufDyn to PathBuf"),
|
let urn = (urn_count + urn_diff) as usize;
|
||||||
(uri_count + rest_diff + urn_diff) as usize,
|
|
||||||
(urn_count + urn_diff) as usize,
|
|
||||||
)
|
|
||||||
.expect("Failed to create Loc from expanded path");
|
|
||||||
|
|
||||||
let expanded = match url {
|
let expanded = match url {
|
||||||
Url::Regular(_) => UrlBuf::Regular(loc),
|
Url::Regular(_) => UrlBuf::Regular(
|
||||||
Url::Search { domain, .. } => UrlBuf::Search { loc, domain: domain.intern() },
|
LocBuf::<std::path::PathBuf>::with(path.into_os().unwrap(), uri, urn).unwrap(),
|
||||||
Url::Archive { domain, .. } => UrlBuf::Archive { loc, domain: domain.intern() },
|
),
|
||||||
Url::Sftp { domain, .. } => {
|
Url::Search { domain, .. } => UrlBuf::Search {
|
||||||
todo!();
|
loc: LocBuf::<std::path::PathBuf>::with(path.into_os().unwrap(), uri, urn).unwrap(),
|
||||||
// UrlBuf::Sftp { loc, domain: domain.intern() }
|
domain: domain.intern(),
|
||||||
}
|
},
|
||||||
|
Url::Archive { domain, .. } => UrlBuf::Archive {
|
||||||
|
loc: LocBuf::<std::path::PathBuf>::with(path.into_os().unwrap(), uri, urn).unwrap(),
|
||||||
|
domain: domain.intern(),
|
||||||
|
},
|
||||||
|
Url::Sftp { domain, .. } => UrlBuf::Sftp {
|
||||||
|
loc: LocBuf::<typed_path::UnixPathBuf>::with(path.into_unix().unwrap(), uri, urn).unwrap(),
|
||||||
|
domain: domain.intern(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
absolute_url(expanded)
|
absolute_url(expanded)
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,32 @@
|
||||||
use std::{borrow::Cow, path::{Path, PathBuf}};
|
use std::{borrow::Cow, path::{Path, PathBuf}};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use percent_encoding::{AsciiSet, CONTROLS, percent_decode, percent_encode};
|
use percent_encoding::{AsciiSet, CONTROLS, percent_decode, percent_encode};
|
||||||
use yazi_shared::path::PathDyn;
|
use yazi_shared::path::{PathCow, PathDyn, PathKind};
|
||||||
|
|
||||||
const SET: &AsciiSet =
|
const SET: &AsciiSet =
|
||||||
&CONTROLS.add(b'"').add(b'*').add(b':').add(b'<').add(b'>').add(b'?').add(b'\\').add(b'|');
|
&CONTROLS.add(b'"').add(b'*').add(b':').add(b'<').add(b'>').add(b'?').add(b'\\').add(b'|');
|
||||||
|
|
||||||
pub trait PercentEncoding {
|
pub trait PercentEncoding<'a> {
|
||||||
fn percent_encode(&self) -> Cow<'_, Path>;
|
fn percent_encode(self) -> Cow<'a, Path>;
|
||||||
|
|
||||||
fn percent_decode(&self) -> Cow<'_, [u8]>;
|
fn percent_decode<K>(self, kind: K) -> Result<PathCow<'a>>
|
||||||
|
where
|
||||||
|
K: Into<PathKind>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PercentEncoding for Path {
|
impl<'a> PercentEncoding<'a> for PathDyn<'a> {
|
||||||
fn percent_encode(&self) -> Cow<'_, Path> {
|
fn percent_encode(self) -> Cow<'a, Path> {
|
||||||
match percent_encode(self.as_os_str().as_encoded_bytes(), SET).into() {
|
match percent_encode(self.encoded_bytes(), SET).into() {
|
||||||
Cow::Borrowed(_) => self.into(),
|
Cow::Borrowed(s) => Path::new(s).into(),
|
||||||
Cow::Owned(s) => PathBuf::from(s).into(),
|
Cow::Owned(s) => PathBuf::from(s).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn percent_decode(&self) -> Cow<'_, [u8]> {
|
fn percent_decode<K>(self, kind: K) -> Result<PathCow<'a>>
|
||||||
match percent_decode(self.as_os_str().as_encoded_bytes()).into() {
|
where
|
||||||
Cow::Borrowed(_) => self.as_os_str().as_encoded_bytes().into(),
|
K: Into<PathKind>,
|
||||||
Cow::Owned(s) => s.into(),
|
{
|
||||||
}
|
PathCow::with(kind, percent_decode(self.encoded_bytes()))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PercentEncoding for PathDyn<'_> {
|
|
||||||
fn percent_encode(&self) -> Cow<'_, Path> {
|
|
||||||
match self {
|
|
||||||
PathDyn::Os(p) => p.percent_encode(),
|
|
||||||
PathDyn::Unix(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn percent_decode(&self) -> Cow<'_, [u8]> {
|
|
||||||
match self {
|
|
||||||
PathDyn::Os(p) => p.percent_decode(),
|
|
||||||
PathDyn::Unix(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use yazi_shared::{path::PathDyn, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
use yazi_shared::{path::{AsPath, PathDyn}, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
||||||
|
|
||||||
use crate::{FsHash128, FsScheme};
|
use crate::{FsHash128, FsScheme, path::PercentEncoding};
|
||||||
|
|
||||||
pub trait FsUrl<'a> {
|
pub trait FsUrl<'a> {
|
||||||
fn cache(&self) -> Option<PathBuf>;
|
fn cache(&self) -> Option<PathBuf>;
|
||||||
|
|
@ -26,13 +26,12 @@ impl<'a> FsUrl<'a> for Url<'a> {
|
||||||
fn cache(&self) -> Option<PathBuf> {
|
fn cache(&self) -> Option<PathBuf> {
|
||||||
fn with_loc(loc: PathDyn, mut root: PathBuf) -> PathBuf {
|
fn with_loc(loc: PathDyn, mut root: PathBuf) -> PathBuf {
|
||||||
let mut it = loc.components();
|
let mut it = loc.components();
|
||||||
todo!();
|
if it.next() == Some(yazi_shared::path::Component::RootDir) {
|
||||||
// if it.next() == Some(std::path::Component::RootDir) {
|
root.push(it.as_path().percent_encode());
|
||||||
// root.push(it.as_path().percent_encode());
|
} else {
|
||||||
// } else {
|
root.push(".%2F");
|
||||||
// root.push(".%2F");
|
root.push(loc.percent_encode());
|
||||||
// root.push(loc.percent_encode());
|
}
|
||||||
// }
|
|
||||||
root
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{any::TypeId, ffi::OsStr, io, process::Stdio};
|
||||||
use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, MetaMethod, Table, UserData, Value};
|
use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, MetaMethod, Table, UserData, Value};
|
||||||
use tokio::process::{ChildStderr, ChildStdin, ChildStdout};
|
use tokio::process::{ChildStderr, ChildStdin, ChildStdout};
|
||||||
use yazi_binding::Error;
|
use yazi_binding::Error;
|
||||||
use yazi_shared::FromWtf8;
|
use yazi_shared::wtf8::FromWtf8;
|
||||||
|
|
||||||
use super::{Child, output::Output};
|
use super::{Child, output::Output};
|
||||||
use crate::process::Status;
|
use crate::process::Status;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#![allow(clippy::option_map_unit_fn)]
|
#![allow(clippy::option_map_unit_fn)]
|
||||||
|
|
||||||
yazi_macro::mod_pub!(data errors event loc path pool scheme shell strand translit url);
|
yazi_macro::mod_pub!(data errors event loc path pool scheme shell strand translit url wtf8);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(alias bytes chars condition debounce either env id layer natsort os predictor ro_cell source sync_cell terminal tests throttle time utf8 wtf8);
|
yazi_macro::mod_flat!(alias bytes chars condition debounce either env id layer natsort os predictor ro_cell source sync_cell terminal tests throttle time utf8);
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
pool::init();
|
pool::init();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::{ffi::OsString, hash::{Hash, Hasher}};
|
||||||
|
|
||||||
use hashbrown::Equivalent;
|
use hashbrown::Equivalent;
|
||||||
|
|
||||||
use crate::{FromWtf8, FromWtf8Vec, path::{AsPath, Component, PathDyn, PathDynError, PathKind, SetNameError}, strand::{AsStrand, Strand}};
|
use crate::{path::{AsPath, Component, PathDyn, PathDynError, PathKind, SetNameError}, strand::AsStrand, wtf8::FromWtf8Vec};
|
||||||
|
|
||||||
// --- PathBufDyn
|
// --- PathBufDyn
|
||||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Eq)]
|
||||||
pub enum PathBufDyn {
|
pub enum PathBufDyn {
|
||||||
Os(std::path::PathBuf),
|
Os(std::path::PathBuf),
|
||||||
Unix(typed_path::UnixPathBuf),
|
Unix(typed_path::UnixPathBuf),
|
||||||
|
|
@ -35,6 +35,16 @@ impl TryFrom<PathBufDyn> for typed_path::UnixPathBuf {
|
||||||
fn try_from(value: PathBufDyn) -> Result<Self, Self::Error> { value.into_unix() }
|
fn try_from(value: PathBufDyn) -> Result<Self, Self::Error> { value.into_unix() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Hash
|
||||||
|
impl Hash for PathBufDyn {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) { self.as_path().hash(state) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- PartialEq
|
||||||
|
impl PartialEq for PathBufDyn {
|
||||||
|
fn eq(&self, other: &PathBufDyn) -> bool { self.as_path() == other.as_path() }
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq<PathDyn<'_>> for PathBufDyn {
|
impl PartialEq<PathDyn<'_>> for PathBufDyn {
|
||||||
fn eq(&self, other: &PathDyn<'_>) -> bool { self.as_path() == *other }
|
fn eq(&self, other: &PathDyn<'_>) -> bool { self.as_path() == *other }
|
||||||
}
|
}
|
||||||
|
|
@ -128,14 +138,10 @@ impl PathBufDyn {
|
||||||
where
|
where
|
||||||
T: AsStrand,
|
T: AsStrand,
|
||||||
{
|
{
|
||||||
Ok(match (self, name.as_strand()) {
|
let s = name.as_strand();
|
||||||
(Self::Os(p), Strand::Os(s)) => p.set_file_name(s),
|
Ok(match self {
|
||||||
(Self::Os(p), Strand::Utf8(s)) => p.set_file_name(s),
|
Self::Os(p) => p.set_file_name(s.as_os()?),
|
||||||
(Self::Os(p), Strand::Bytes(b)) => {
|
Self::Unix(p) => p.set_file_name(s.encoded_bytes()),
|
||||||
p.set_file_name(OsStr::from_wtf8(b).map_err(|_| SetNameError::FromWtf8)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Self::Unix(p), s) => p.set_file_name(s.encoded_bytes()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,15 @@ pub enum Component<'a> {
|
||||||
Normal(Strand<'a>),
|
Normal(Strand<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Component<'a> {
|
||||||
|
pub fn as_normal(&self) -> Option<Strand<'a>> {
|
||||||
|
match self {
|
||||||
|
Self::Normal(s) => Some(*s),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<std::path::Component<'a>> for Component<'a> {
|
impl<'a> From<std::path::Component<'a>> for Component<'a> {
|
||||||
fn from(value: std::path::Component<'a>) -> Self {
|
fn from(value: std::path::Component<'a>) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,15 @@ use crate::strand::StrandError;
|
||||||
// --- EndsWithError
|
// --- EndsWithError
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("calling ends_with on paths with different encodings")]
|
#[error("calling ends_with on paths with different encodings")]
|
||||||
pub struct EndsWithError;
|
pub enum EndsWithError {
|
||||||
|
FromStrand(#[from] StrandError),
|
||||||
|
}
|
||||||
|
|
||||||
// --- JoinError
|
// --- JoinError
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("calling join on paths with different encodings")]
|
#[error("calling join on paths with different encodings")]
|
||||||
pub enum JoinError {
|
pub enum JoinError {
|
||||||
FromWtf8,
|
FromStrand(#[from] StrandError),
|
||||||
FromPathDyn(#[from] PathDynError),
|
FromPathDyn(#[from] PathDynError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +49,6 @@ impl From<PathDynError> for std::io::Error {
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("calling set_name on paths with different encodings")]
|
#[error("calling set_name on paths with different encodings")]
|
||||||
pub enum SetNameError {
|
pub enum SetNameError {
|
||||||
FromWtf8,
|
|
||||||
FromStrand(#[from] StrandError),
|
FromStrand(#[from] StrandError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +80,9 @@ impl From<StrandError> for RsplitOnceError {
|
||||||
// --- StartsWithError
|
// --- StartsWithError
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("calling starts_with on paths with different encodings")]
|
#[error("calling starts_with on paths with different encodings")]
|
||||||
pub struct StartsWithError;
|
pub enum StartsWithError {
|
||||||
|
FromStrand(#[from] StrandError),
|
||||||
|
}
|
||||||
|
|
||||||
// --- StripPrefixError
|
// --- StripPrefixError
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -92,6 +95,14 @@ pub enum StripPrefixError {
|
||||||
WrongEncoding,
|
WrongEncoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<StrandError> for StripPrefixError {
|
||||||
|
fn from(err: StrandError) -> Self {
|
||||||
|
match err {
|
||||||
|
StrandError::AsOs | StrandError::AsUtf8 => Self::WrongEncoding,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<std::path::StripPrefixError> for StripPrefixError {
|
impl From<std::path::StripPrefixError> for StripPrefixError {
|
||||||
fn from(_: std::path::StripPrefixError) -> Self { Self::NotPrefix }
|
fn from(_: std::path::StripPrefixError) -> Self { Self::NotPrefix }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ use anyhow::Result;
|
||||||
use hashbrown::Equivalent;
|
use hashbrown::Equivalent;
|
||||||
|
|
||||||
use super::{RsplitOnceError, StartsWithError};
|
use super::{RsplitOnceError, StartsWithError};
|
||||||
use crate::{BytesExt, FromWtf8, Utf8BytePredictor, path::{AsPath, Components, Display, EndsWithError, JoinError, PathBufDyn, PathDynError, PathKind, StripPrefixError}, strand::{AsStrand, Strand, StrandError}};
|
use crate::{BytesExt, Utf8BytePredictor, path::{AsPath, Components, Display, EndsWithError, JoinError, PathBufDyn, PathDynError, PathKind, StripPrefixError}, strand::{AsStrand, Strand, StrandError}};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
pub enum PathDyn<'p> {
|
pub enum PathDyn<'p> {
|
||||||
Os(&'p std::path::Path),
|
Os(&'p std::path::Path),
|
||||||
Unix(&'p typed_path::UnixPath),
|
Unix(&'p typed_path::UnixPath),
|
||||||
|
|
@ -186,14 +186,10 @@ impl<'p> PathDyn<'p> {
|
||||||
where
|
where
|
||||||
T: AsStrand,
|
T: AsStrand,
|
||||||
{
|
{
|
||||||
Ok(match (self, child.as_strand()) {
|
let s = child.as_strand();
|
||||||
(Self::Os(p), Strand::Os(q)) => p.ends_with(q),
|
Ok(match self {
|
||||||
(Self::Os(p), Strand::Utf8(q)) => p.ends_with(q),
|
Self::Os(p) => p.ends_with(s.as_os()?),
|
||||||
(Self::Os(p), Strand::Bytes(b)) => {
|
Self::Unix(p) => p.ends_with(s.encoded_bytes()),
|
||||||
p.ends_with(OsStr::from_wtf8(b).map_err(|_| EndsWithError)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Self::Unix(p), s) => p.ends_with(s.encoded_bytes()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,14 +197,10 @@ impl<'p> PathDyn<'p> {
|
||||||
where
|
where
|
||||||
T: AsStrand,
|
T: AsStrand,
|
||||||
{
|
{
|
||||||
Ok(match (self, path.as_strand()) {
|
let s = path.as_strand();
|
||||||
(Self::Os(p), Strand::Os(q)) => PathBufDyn::Os(p.join(q)),
|
Ok(match self {
|
||||||
(Self::Os(p), Strand::Utf8(q)) => PathBufDyn::Os(p.join(q)),
|
Self::Os(p) => PathBufDyn::Os(p.join(s.as_os()?)),
|
||||||
(Self::Os(p), Strand::Bytes(b)) => {
|
Self::Unix(p) => PathBufDyn::Unix(p.join(s.encoded_bytes())),
|
||||||
PathBufDyn::Os(p.join(OsStr::from_wtf8(b).map_err(|_| JoinError::FromWtf8)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
(Self::Unix(p), s) => PathBufDyn::Unix(p.join(s.encoded_bytes())),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,14 +227,10 @@ impl<'p> PathDyn<'p> {
|
||||||
where
|
where
|
||||||
T: AsStrand,
|
T: AsStrand,
|
||||||
{
|
{
|
||||||
Ok(match (self, base.as_strand()) {
|
let s = base.as_strand();
|
||||||
(Self::Os(p), Strand::Os(s)) => p.starts_with(s),
|
Ok(match self {
|
||||||
(Self::Os(p), Strand::Utf8(s)) => p.starts_with(s),
|
Self::Os(p) => p.starts_with(s.as_os()?),
|
||||||
(Self::Os(p), Strand::Bytes(b)) => {
|
Self::Unix(p) => p.starts_with(s.encoded_bytes()),
|
||||||
p.starts_with(OsStr::from_wtf8(b).map_err(|_| StartsWithError)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Self::Unix(p), s) => p.starts_with(s.encoded_bytes()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,14 +238,10 @@ impl<'p> PathDyn<'p> {
|
||||||
where
|
where
|
||||||
T: AsStrand,
|
T: AsStrand,
|
||||||
{
|
{
|
||||||
Ok(match (self, base.as_strand()) {
|
let s = base.as_strand();
|
||||||
(Self::Os(p), Strand::Os(s)) => Self::Os(p.strip_prefix(s)?),
|
Ok(match self {
|
||||||
(Self::Os(p), Strand::Utf8(s)) => Self::Os(p.strip_prefix(s)?),
|
Self::Os(p) => Self::Os(p.strip_prefix(s.as_os()?)?),
|
||||||
(Self::Os(p), Strand::Bytes(b)) => {
|
Self::Unix(p) => Self::Unix(p.strip_prefix(s.encoded_bytes())?),
|
||||||
Self::Os(p.strip_prefix(OsStr::from_wtf8(b).map_err(|_| StripPrefixError::WrongEncoding)?)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Self::Unix(p), s) => Self::Unix(p.strip_prefix(s.encoded_bytes())?),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ pub fn escape_os_bytes(b: &[u8]) -> Cow<'_, [u8]> {
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn escape_os_str(s: &std::ffi::OsStr) -> Cow<'_, std::ffi::OsStr> {
|
pub fn escape_os_str(s: &std::ffi::OsStr) -> Cow<'_, std::ffi::OsStr> {
|
||||||
use crate::FromWtf8Vec;
|
use crate::wtf8::FromWtf8Vec;
|
||||||
|
|
||||||
match escape_os_bytes(s.as_encoded_bytes()) {
|
match escape_os_bytes(s.as_encoded_bytes()) {
|
||||||
Cow::Borrowed(_) => Cow::Borrowed(s),
|
Cow::Borrowed(_) => Cow::Borrowed(s),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsString, hash::{Hash, Hasher}};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{FromWtf8Vec, path::PathDyn, strand::{AsStrand, Strand, StrandCow, StrandError, StrandKind}};
|
use crate::{path::PathDyn, strand::{AsStrand, Strand, StrandCow, StrandError, StrandKind}, wtf8::FromWtf8Vec};
|
||||||
|
|
||||||
// --- StrandBuf
|
// --- StrandBuf
|
||||||
#[derive(Clone, Debug, Eq)]
|
#[derive(Clone, Debug, Eq)]
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@ impl AsStrand for &std::path::Path {
|
||||||
fn as_strand(&self) -> Strand<'_> { Strand::Os(self.as_os_str()) }
|
fn as_strand(&self) -> Strand<'_> { Strand::Os(self.as_os_str()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsStrand for &std::path::PathBuf {
|
||||||
|
fn as_strand(&self) -> Strand<'_> { Strand::Os(self.as_os_str()) }
|
||||||
|
}
|
||||||
|
|
||||||
impl AsStrand for &typed_path::UnixPath {
|
impl AsStrand for &typed_path::UnixPath {
|
||||||
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self.as_bytes()) }
|
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self.as_bytes()) }
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +59,10 @@ impl AsStrand for crate::path::Components<'_> {
|
||||||
fn as_strand(&self) -> Strand<'_> { self.strand() }
|
fn as_strand(&self) -> Strand<'_> { self.strand() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsStrand for Cow<'_, [u8]> {
|
||||||
|
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
|
||||||
|
}
|
||||||
|
|
||||||
impl AsStrand for Cow<'_, OsStr> {
|
impl AsStrand for Cow<'_, OsStr> {
|
||||||
fn as_strand(&self) -> Strand<'_> { Strand::Os(self) }
|
fn as_strand(&self) -> Strand<'_> { Strand::Os(self) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::Display};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{BytesExt, FromWtf8, strand::{AsStrand, StrandBuf, StrandError, StrandKind}};
|
use crate::{BytesExt, strand::{AsStrand, StrandBuf, StrandError, StrandKind}, wtf8::FromWtf8};
|
||||||
|
|
||||||
// --- Strand
|
// --- Strand
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::{borrow::Cow, fmt::{Debug, Formatter}, path::{Path, PathBuf}, str::FromStr};
|
use std::{borrow::Cow, fmt::{Debug, Formatter}, hash::{Hash, Hasher}, path::{Path, PathBuf}, str::FromStr};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{loc::LocBuf, path::{PathBufDyn, PathDynError, SetNameError}, pool::{InternStr, Pool, Symbol}, scheme::{Scheme, SchemeKind}, strand::AsStrand, url::{AsUrl, Url, UrlCow, UrlLike}};
|
use crate::{loc::LocBuf, path::{PathBufDyn, PathDynError, SetNameError}, pool::{InternStr, Pool, Symbol}, scheme::{Scheme, SchemeKind}, strand::AsStrand, url::{AsUrl, Url, UrlCow, UrlLike}};
|
||||||
|
|
||||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
#[derive(Clone, Eq)]
|
||||||
pub enum UrlBuf {
|
pub enum UrlBuf {
|
||||||
Regular(LocBuf),
|
Regular(LocBuf),
|
||||||
Search { loc: LocBuf, domain: Symbol<str> },
|
Search { loc: LocBuf, domain: Symbol<str> },
|
||||||
|
|
@ -92,6 +92,10 @@ impl From<Cow<'_, Self>> for UrlBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Eq
|
// --- Eq
|
||||||
|
impl PartialEq for UrlBuf {
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.as_url() == other.as_url() }
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq<Url<'_>> for UrlBuf {
|
impl PartialEq<Url<'_>> for UrlBuf {
|
||||||
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +104,11 @@ impl PartialEq<Url<'_>> for &UrlBuf {
|
||||||
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Hash
|
||||||
|
impl Hash for UrlBuf {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) { self.as_url().hash(state) }
|
||||||
|
}
|
||||||
|
|
||||||
impl UrlBuf {
|
impl UrlBuf {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> &'static Self {
|
pub fn new() -> &'static Self {
|
||||||
|
|
|
||||||
|
|
@ -32,17 +32,17 @@ impl<'a> Components<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||||
let Ok(path) = self.inner.path().as_os() else {
|
let Ok(os) = self.inner.strand().as_os() else {
|
||||||
return OsString::from(EncodeUrl(self.url()).to_string()).into();
|
return OsString::from(EncodeUrl(self.url()).to_string()).into();
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.url.is_regular() || self.scheme_yielded {
|
if self.url.is_regular() || self.scheme_yielded {
|
||||||
return path.as_os_str().into();
|
return os.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut s = OsString::from(EncodeScheme(self.url()).to_string());
|
let mut s = OsString::from(EncodeScheme(self.url()).to_string());
|
||||||
s.reserve_exact(path.as_os_str().len());
|
s.reserve_exact(os.len());
|
||||||
s.push(path);
|
s.push(os);
|
||||||
s.into()
|
s.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
yazi-shared/src/wtf8/mod.rs
Normal file
1
yazi-shared/src/wtf8/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
yazi_macro::mod_flat!(validator wtf8);
|
||||||
116
yazi-shared/src/wtf8/validator.rs
Normal file
116
yazi-shared/src/wtf8/validator.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub(super) fn valid_wtf8(bytes: &[u8]) -> bool {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
let b = bytes[i];
|
||||||
|
if b < 0b1000_0000 {
|
||||||
|
// ASCII
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-byte: 110x_xxxx 10xx_xxxx
|
||||||
|
// first byte must be >= 0b1100_0010 to forbid overlongs
|
||||||
|
if (b & 0b1110_0000) == 0b1100_0000 {
|
||||||
|
if b < 0b1100_0010 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if i + 1 >= bytes.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bytes[i + 1] & 0b1100_0000) != 0b1000_0000 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3-byte: 1110_xxxx 10xx_xxxx 10xx_xxxx
|
||||||
|
if (b & 0b1111_0000) == 0b1110_0000 {
|
||||||
|
if i + 2 >= bytes.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let (b1, b2) = (bytes[i + 1], bytes[i + 2]);
|
||||||
|
if (b1 & 0b1100_0000) != 0b1000_0000 || (b2 & 0b1100_0000) != 0b1000_0000 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if b == 0b1110_0000 && b1 < 0b1010_0000 {
|
||||||
|
// to forbid overlongs, second byte must be >= 0xA0
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4-byte: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||||
|
if (b & 0b1111_1000) == 0b1111_0000 {
|
||||||
|
if b > 0b1111_0100 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if i + 3 >= bytes.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let (b1, b2, b3) = (bytes[i + 1], bytes[i + 2], bytes[i + 3]);
|
||||||
|
if (b1 & 0b1100_0000) != 0b1000_0000
|
||||||
|
|| (b2 & 0b1100_0000) != 0b1000_0000
|
||||||
|
|| (b3 & 0b1100_0000) != 0b1000_0000
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if b == 0b1111_0000 && b1 < 0b1001_0000 {
|
||||||
|
// to forbid overlongs for > U+FFFF, second byte must be >= 0x90
|
||||||
|
return false;
|
||||||
|
} else if b == 0b1111_0100 && b1 > 0b1000_1111 {
|
||||||
|
// to stay <= U+10FFFF, second byte must be <= 0x8F
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_valid_wtf8() {
|
||||||
|
let cases: &[(&[u8], bool)] = &[
|
||||||
|
// Valid ASCII
|
||||||
|
(b"hello", true),
|
||||||
|
// Valid 2-byte UTF-8
|
||||||
|
(&[0xc2, 0xa0], true), // U+00A0
|
||||||
|
// Invalid 2-byte: overlong encoding
|
||||||
|
(&[0xc0, 0x80], false), // overlong for U+0000
|
||||||
|
(&[0xc1, 0xbf], false), // overlong for U+007F
|
||||||
|
// Valid 3-byte UTF-8
|
||||||
|
(&[0xe0, 0xa0, 0x80], true), // U+0800
|
||||||
|
// Invalid 3-byte: overlong encoding
|
||||||
|
(&[0xe0, 0x9f, 0xbf], false), // overlong for U+07FF
|
||||||
|
// WTF-8 specific: unpaired surrogates (should be valid in WTF-8)
|
||||||
|
((&[0xed, 0xa0, 0x80]), true), // U+D800 = ED A0 80 (high surrogate)
|
||||||
|
((&[0xed, 0xbf, 0xbf]), true), // U+DFFF = ED BF BF (low surrogate)
|
||||||
|
// Valid 4-byte UTF-8
|
||||||
|
((&[0xf0, 0x90, 0x80, 0x80]), true), // U+10000
|
||||||
|
// Invalid 4-byte: overlong
|
||||||
|
((&[0xf0, 0x8f, 0xbf, 0xbf]), false), // overlong for U+FFFF
|
||||||
|
// Invalid 4-byte: beyond U+10FFFF
|
||||||
|
((&[0xf4, 0x90, 0x80, 0x80]), false), // U+110000
|
||||||
|
// Invalid continuation byte
|
||||||
|
((&[0xc2, 0x00]), false),
|
||||||
|
// Incomplete sequence
|
||||||
|
((&[0xc2]), false),
|
||||||
|
((&[0xe0, 0xa0]), false),
|
||||||
|
((&[0xf0, 0x90, 0x80]), false),
|
||||||
|
];
|
||||||
|
|
||||||
|
for &(input, expected) in cases {
|
||||||
|
assert_eq!(valid_wtf8(input), expected, "input: {:?}", input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,8 +35,11 @@ impl FromWtf8 for OsStr {
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
// FIXME: validate WTF-8
|
if super::valid_wtf8(wtf8) {
|
||||||
Ok(unsafe { Self::from_encoded_bytes_unchecked(wtf8) })
|
Ok(unsafe { Self::from_encoded_bytes_unchecked(wtf8) })
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("Invalid WTF-8 sequence"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61,8 +64,11 @@ impl FromWtf8Vec for OsString {
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
// FIXME: validate WTF-8
|
if super::valid_wtf8(&wtf8) {
|
||||||
Ok(unsafe { Self::from_encoded_bytes_unchecked(wtf8) })
|
Ok(unsafe { Self::from_encoded_bytes_unchecked(wtf8) })
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("Invalid WTF-8 sequence"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{io, path::Path, sync::Arc};
|
use std::{io, sync::Arc};
|
||||||
|
|
||||||
use tokio::io::{AsyncWriteExt, BufReader, BufWriter};
|
use tokio::io::{AsyncWriteExt, BufReader, BufWriter};
|
||||||
use yazi_config::vfs::{ProviderSftp, Vfs};
|
use yazi_config::vfs::{ProviderSftp, Vfs};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use percent_encoding::percent_decode;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use yazi_shared::{scheme::SchemeKind, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
|
use yazi_shared::{scheme::SchemeKind, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
|
||||||
|
|
||||||
|
|
@ -24,8 +27,8 @@ impl Reporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_local<'a>(&self, url: UrlCow<'a>) {
|
fn report_local(&self, url: UrlCow) {
|
||||||
let Some((parent, name)) = url.pair() else { return };
|
let Some((parent, urn)) = url.pair() else { return };
|
||||||
|
|
||||||
// FIXME: LINKED should return Url instead of Path
|
// FIXME: LINKED should return Url instead of Path
|
||||||
let linked = LINKED.read();
|
let linked = LINKED.read();
|
||||||
|
|
@ -37,19 +40,22 @@ impl Reporter {
|
||||||
self.local_tx.send(url.to_owned()).ok();
|
self.local_tx.send(url.to_owned()).ok();
|
||||||
self.local_tx.send(parent.to_owned()).ok();
|
self.local_tx.send(parent.to_owned()).ok();
|
||||||
}
|
}
|
||||||
if name.ext().is_some_and(|e| e == "%tmp") {
|
|
||||||
|
if urn.ext().is_some_and(|e| e == "%tmp") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// SFTP caches
|
|
||||||
// todo!();
|
// Virtual caches
|
||||||
// if let Some(dir) = watched.find_by_cache(&parent.loc()) {
|
let Some(dir) = watched.find_by_cache(parent.loc()) else { continue };
|
||||||
// self.remote_tx.send(dir.join(name)).ok();
|
let Some(name) = url.name() else { continue };
|
||||||
// self.remote_tx.send(dir.to_owned()).ok();
|
if let Ok(u) = dir.try_join(Cow::from(percent_decode(name.encoded_bytes()))) {
|
||||||
// }
|
self.remote_tx.send(u).ok();
|
||||||
|
}
|
||||||
|
self.remote_tx.send(dir).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_remote<'a>(&self, url: UrlCow<'a>) {
|
fn report_remote(&self, url: UrlCow) {
|
||||||
let Some(parent) = url.parent() else { return };
|
let Some(parent) = url.parent() else { return };
|
||||||
if !WATCHED.read().contains(parent) {
|
if !WATCHED.read().contains(parent) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::path::Path;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use percent_encoding::percent_decode_str;
|
use percent_encoding::percent_decode_str;
|
||||||
use yazi_fs::{Xdg, path::PercentEncoding};
|
use yazi_fs::{Xdg, path::PercentEncoding};
|
||||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
use yazi_shared::{path::{Component, PathBufDyn, PathDyn, PathLike}, pool::InternStr, scheme::SchemeKind, url::{AsUrl, UrlBuf, UrlLike}};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Watched(HashSet<UrlBuf>);
|
pub struct Watched(HashSet<UrlBuf>);
|
||||||
|
|
@ -28,28 +28,24 @@ impl Watched {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn remove(&mut self, url: impl AsUrl) { self.0.remove(&url.as_url()); }
|
pub(crate) fn remove(&mut self, url: impl AsUrl) { self.0.remove(&url.as_url()); }
|
||||||
|
|
||||||
pub(super) fn find_by_cache(&self, cache: &Path) -> Option<&UrlBuf> {
|
pub(super) fn find_by_cache(&self, cache: PathDyn) -> Option<UrlBuf> {
|
||||||
let mut it = cache.strip_prefix(Xdg::cache_dir()).ok()?.components();
|
let mut it = cache.try_strip_prefix(Xdg::cache_dir()).ok()?.components();
|
||||||
|
|
||||||
let l1 = it.next()?.as_os_str().to_str()?;
|
// Parse domain
|
||||||
let (l2, rel) =
|
let domain = it.next()?.as_normal()?.to_str().ok()?;
|
||||||
if let Ok(p) = it.as_path().strip_prefix(".%2F") { (p, true) } else { (it.as_path(), false) };
|
let domain = percent_decode_str(domain.strip_prefix("sftp-")?).decode_utf8().ok()?.intern();
|
||||||
|
|
||||||
let domain = percent_decode_str(l1.strip_prefix("sftp-")?).decode_utf8().ok()?;
|
// Parse path
|
||||||
let loc = l2.percent_decode();
|
let (path, abs) =
|
||||||
|
if let Ok(p) = it.path().try_strip_prefix(".%2F") { (p, false) } else { (it.path(), true) };
|
||||||
|
let path = path.percent_decode(SchemeKind::Sftp).ok()?;
|
||||||
|
let path = PathBufDyn::from_components(
|
||||||
|
SchemeKind::Sftp,
|
||||||
|
Some(Component::RootDir).filter(|_| abs).into_iter().chain(path.components()),
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
self.0.iter().find(|u| {
|
let url = UrlBuf::Sftp { loc: path.into_unix().ok()?.into(), domain };
|
||||||
todo!()
|
if self.contains(&url) { Some(url) } else { None }
|
||||||
// if u.scheme != SchemeRef::Sftp(&domain) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut it = u.loc.components();
|
|
||||||
// if it.next() == Some(std::path::Component::RootDir) {
|
|
||||||
// !rel && it.as_path().as_os_str().as_encoded_bytes() == loc.as_ref()
|
|
||||||
// } else {
|
|
||||||
// rel && u.loc.as_os_str().as_encoded_bytes() == loc.as_ref()
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue