mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: new Unix path backend (#3359)
This commit is contained in:
parent
a08b345a02
commit
e5ee31cb7e
76 changed files with 1430 additions and 979 deletions
67
Cargo.lock
generated
67
Cargo.lock
generated
|
|
@ -505,9 +505,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.46"
|
||||
version = "1.2.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
|
||||
checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
|
|
@ -1544,14 +1544,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1712,12 +1713,12 @@ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.0"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
|
@ -2040,7 +2041,7 @@ version = "0.16.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f"
|
||||
dependencies = [
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3468,9 +3469,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
|
@ -3633,9 +3634,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.110"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -3961,6 +3962,12 @@ dependencies = [
|
|||
"rand 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-path"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7922f2cdc51280d47b491af9eafc41eb0cdab85eabcb390c854412fcbf26dbe8"
|
||||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
version = "1.0.3"
|
||||
|
|
@ -4645,7 +4652,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"crossterm 0.29.0",
|
||||
"futures",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"libc",
|
||||
"mlua",
|
||||
"paste",
|
||||
|
|
@ -4698,7 +4705,7 @@ name = "yazi-binding"
|
|||
version = "25.9.15"
|
||||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"mlua",
|
||||
"paste",
|
||||
"ratatui",
|
||||
|
|
@ -4722,7 +4729,7 @@ dependencies = [
|
|||
"clap_complete_fig",
|
||||
"clap_complete_nushell",
|
||||
"futures",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"regex",
|
||||
"serde",
|
||||
"vergen-gitcl",
|
||||
|
|
@ -4781,7 +4788,7 @@ dependencies = [
|
|||
"crossterm 0.29.0",
|
||||
"dirs",
|
||||
"globset",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"indexmap",
|
||||
"ratatui",
|
||||
"regex",
|
||||
|
|
@ -4802,7 +4809,7 @@ version = "25.9.15"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"crossterm 0.29.0",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"indexmap",
|
||||
"parking_lot",
|
||||
"ratatui",
|
||||
|
|
@ -4831,7 +4838,7 @@ name = "yazi-dds"
|
|||
version = "25.9.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"mlua",
|
||||
"ordered-float 5.1.0",
|
||||
"parking_lot",
|
||||
|
|
@ -4913,11 +4920,12 @@ dependencies = [
|
|||
"core-foundation-sys",
|
||||
"dirs",
|
||||
"foldhash 0.2.0",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"libc",
|
||||
"objc2",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"rand 0.9.2",
|
||||
"regex",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
|
|
@ -4947,7 +4955,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"bitflags 2.10.0",
|
||||
"crossterm 0.29.0",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"mlua",
|
||||
"ordered-float 5.1.0",
|
||||
"serde",
|
||||
|
|
@ -4967,7 +4975,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"crossterm 0.29.0",
|
||||
"futures",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"libc",
|
||||
"mlua",
|
||||
"parking_lot",
|
||||
|
|
@ -5025,7 +5033,7 @@ dependencies = [
|
|||
"async-priority-channel",
|
||||
"foldhash 0.2.0",
|
||||
"futures",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"libc",
|
||||
"lru 0.16.2",
|
||||
"mlua",
|
||||
|
|
@ -5066,7 +5074,7 @@ dependencies = [
|
|||
"crossterm 0.29.0",
|
||||
"foldhash 0.2.0",
|
||||
"futures",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ordered-float 5.1.0",
|
||||
|
|
@ -5075,6 +5083,7 @@ dependencies = [
|
|||
"serde",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"typed-path",
|
||||
"uzers",
|
||||
"windows-sys 0.61.2",
|
||||
"yazi-macro",
|
||||
|
|
@ -5108,7 +5117,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"deadpool",
|
||||
"futures",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"parking_lot",
|
||||
"russh",
|
||||
"tokio",
|
||||
|
|
@ -5125,7 +5134,7 @@ name = "yazi-watcher"
|
|||
version = "25.9.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"notify",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
|
|
@ -5164,18 +5173,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
version = "0.8.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.27"
|
||||
version = "0.8.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ dirs = "6.0.0"
|
|||
foldhash = "0.2.0"
|
||||
futures = "0.3.31"
|
||||
globset = "0.4.18"
|
||||
hashbrown = { version = "0.16.0", features = [ "serde" ] }
|
||||
indexmap = { version = "2.12.0", features = [ "serde" ] }
|
||||
hashbrown = { version = "0.16.1", features = [ "serde" ] }
|
||||
indexmap = { version = "2.12.1", features = [ "serde" ] }
|
||||
libc = "0.2.177"
|
||||
lru = "0.16.2"
|
||||
mlua = { version = "0.11.4", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
|
||||
|
|
@ -41,6 +41,7 @@ ordered-float = { version = "5.1.0", features = [ "serde" ] }
|
|||
parking_lot = "0.12.5"
|
||||
paste = "1.0.15"
|
||||
percent-encoding = "2.3.2"
|
||||
rand = { version = "0.9.2", default-features = false, features = [ "os_rng", "small_rng", "std" ] }
|
||||
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] }
|
||||
regex = "1.12.2"
|
||||
russh = { version = "0.54.6", default-features = false, features = [ "ring", "rsa" ] }
|
||||
|
|
@ -55,6 +56,7 @@ tokio-util = "0.7.17"
|
|||
toml = { version = "0.9.8" }
|
||||
tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] }
|
||||
twox-hash = { version = "2.1.2", default-features = false, features = [ "std", "random", "xxhash3_128" ] }
|
||||
typed-path = "0.12.0"
|
||||
unicode-width = { version = "0.2.0", default-features = false }
|
||||
uzers = "0.12.1"
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ impl Trigger {
|
|||
|
||||
Some(match path.rsplit_pred(sep) {
|
||||
Some((p, c)) if p.is_empty() => {
|
||||
let root = PathDyn::with(scheme.kind(), MAIN_SEPARATOR_STR).expect("valid root");
|
||||
let root = PathDyn::with_str(scheme.kind(), MAIN_SEPARATOR_STR);
|
||||
(UrlCow::try_from((scheme, root)).ok()?.into_owned(), c.into())
|
||||
}
|
||||
Some((p, c)) => (expand_url(UrlCow::try_from((scheme, p)).ok()?), c.into()),
|
||||
|
|
@ -99,7 +99,7 @@ mod tests {
|
|||
fn compare(s: &str, parent: &str, child: &str) {
|
||||
let (mut p, c) = Trigger::split_url(s).unwrap();
|
||||
if let Ok(u) = p.try_strip_prefix(yazi_fs::CWD.load().as_ref()) {
|
||||
p = UrlBuf::from(u);
|
||||
p = UrlBuf::Regular(u.as_os().unwrap().into());
|
||||
}
|
||||
assert_eq!((p, c.to_str().unwrap()), (parent.parse().unwrap(), child));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Valu
|
|||
use yazi_binding::Style;
|
||||
use yazi_config::THEME;
|
||||
use yazi_plugin::bindings::Range;
|
||||
use yazi_shared::url::UrlLike;
|
||||
use yazi_shared::{path::AsPath, url::UrlLike};
|
||||
|
||||
use super::Lives;
|
||||
use crate::lives::PtrCell;
|
||||
|
|
@ -91,7 +91,7 @@ impl UserData for File {
|
|||
|
||||
let mut comp = me.url.try_strip_prefix(me.url.trail()).unwrap_or(me.url.loc()).components();
|
||||
comp.next_back();
|
||||
Some(lua.create_string(comp.as_path().as_os_str().as_encoded_bytes())).transpose()
|
||||
Some(lua.create_string(comp.as_path().encoded_bytes())).transpose()
|
||||
});
|
||||
methods.add_method("style", |lua, me, ()| {
|
||||
lua.named_registry_value::<AnyUserData>("cx")?.borrow_scoped(|core: &yazi_core::Core| {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ffi::{OsStr, OsString}, hash::Hash, io::{Read, Write}, ops::Deref, path::Path};
|
||||
use std::{hash::Hash, io::{Read, Write}, ops::Deref, path::Path};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use crossterm::{execute, style::Print};
|
||||
|
|
@ -11,7 +11,7 @@ use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider
|
|||
use yazi_macro::{err, succ};
|
||||
use yazi_parser::VoidOpt;
|
||||
use yazi_proxy::{AppProxy, HIDER, TasksProxy};
|
||||
use yazi_shared::{OsStrJoin, data::Data, path::PathDyn, terminal_clear, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, terminal_clear, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_term::tty::TTY;
|
||||
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
||||
use yazi_watcher::WATCHER;
|
||||
|
|
@ -47,7 +47,7 @@ impl Actor for BulkRename {
|
|||
.create_new(true)
|
||||
.open(&tmp)
|
||||
.await?
|
||||
.write_all(old.join(OsStr::new("\n")).as_encoded_bytes())
|
||||
.write_all(old.join(Strand::Utf8("\n")).encoded_bytes())
|
||||
.await?;
|
||||
|
||||
defer! {
|
||||
|
|
@ -160,7 +160,7 @@ impl BulkRename {
|
|||
YAZI.opener.block(YAZI.open.all(Path::new("bulk-rename.txt"), "text/plain"))
|
||||
}
|
||||
|
||||
fn replace_url(url: &UrlBuf, take: usize, rep: &OsStr) -> Result<UrlBuf> {
|
||||
fn replace_url(url: &UrlBuf, take: usize, rep: &StrandBuf) -> Result<UrlBuf> {
|
||||
Ok(url.try_replace(take, PathDyn::with(url.kind(), rep)?)?.into_owned())
|
||||
}
|
||||
|
||||
|
|
@ -220,10 +220,10 @@ impl BulkRename {
|
|||
|
||||
// --- Tuple
|
||||
#[derive(Clone, Debug)]
|
||||
struct Tuple(usize, OsString);
|
||||
struct Tuple(usize, StrandBuf);
|
||||
|
||||
impl Deref for Tuple {
|
||||
type Target = OsStr;
|
||||
type Target = StrandBuf;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.1 }
|
||||
}
|
||||
|
|
@ -238,12 +238,12 @@ impl Hash for Tuple {
|
|||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.1.hash(state); }
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> for Tuple {
|
||||
fn as_ref(&self) -> &OsStr { &self.1 }
|
||||
impl AsStrand for &Tuple {
|
||||
fn as_strand(&self) -> Strand<'_> { self.1.as_strand() }
|
||||
}
|
||||
|
||||
impl Tuple {
|
||||
fn new(index: usize, inner: impl Into<OsString>) -> Self { Self(index, inner.into()) }
|
||||
fn new(index: usize, inner: impl Into<StrandBuf>) -> Self { Self(index, inner.into()) }
|
||||
}
|
||||
|
||||
// --- Tests
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::{Result, bail};
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::mgr::CopyOpt;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
use yazi_shared::{data::Data, strand::ToStrand, url::UrlLike};
|
||||
use yazi_widgets::CLIPBOARD;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
@ -28,11 +28,11 @@ impl Actor for Copy {
|
|||
match opt.r#type.as_ref() {
|
||||
// TODO: rename to "url"
|
||||
"path" => {
|
||||
s.extend_from_slice(&opt.separator.transform(&u.os_str()));
|
||||
s.extend_from_slice(&opt.separator.transform(&u.to_strand()));
|
||||
}
|
||||
"dirname" => {
|
||||
if let Some(p) = u.parent() {
|
||||
s.extend_from_slice(&opt.separator.transform(&p.os_str()));
|
||||
s.extend_from_slice(&opt.separator.transform(&p.to_strand()));
|
||||
}
|
||||
}
|
||||
"filename" => {
|
||||
|
|
@ -50,7 +50,7 @@ impl Actor for Copy {
|
|||
|
||||
// Copy the CWD path regardless even if the directory is empty
|
||||
if s.is_empty() && opt.r#type == "dirname" {
|
||||
s.extend_from_slice(&opt.separator.transform(&cx.cwd().os_str()));
|
||||
s.extend_from_slice(&opt.separator.transform(&cx.cwd().to_strand()));
|
||||
}
|
||||
|
||||
futures::executor::block_on(CLIPBOARD.set(s));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ffi::OsString, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::{select, time};
|
||||
|
|
@ -7,7 +7,7 @@ use yazi_dds::spark::SparkKind;
|
|||
use yazi_macro::{emit, succ};
|
||||
use yazi_parser::mgr::QuitOpt;
|
||||
use yazi_proxy::ConfirmProxy;
|
||||
use yazi_shared::{data::Data, event::EventQuit, url::{AsUrl, UrlCow}};
|
||||
use yazi_shared::{data::Data, event::EventQuit, strand::{Strand, StrandLike, ToStrandJoin}, url::AsUrl};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -66,17 +66,12 @@ impl Actor for Quit {
|
|||
}
|
||||
|
||||
impl Quit {
|
||||
pub(super) fn with_selected<'a, I, T>(selected: I)
|
||||
pub(super) fn with_selected<I>(selected: I)
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<UrlCow<'a>>,
|
||||
I: IntoIterator,
|
||||
I::Item: AsUrl,
|
||||
{
|
||||
let paths = selected.into_iter().fold(OsString::new(), |mut s, u| {
|
||||
s.push(u.into().as_url().os_str());
|
||||
s.push("\n");
|
||||
s
|
||||
});
|
||||
|
||||
let paths = selected.into_iter().join(Strand::Utf8("\n"));
|
||||
if !paths.is_empty() {
|
||||
emit!(Quit(EventQuit { selected: Some(paths), ..Default::default() }));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl Actor for SearchDo {
|
|||
handle.abort();
|
||||
}
|
||||
|
||||
let cwd = tab.cwd().to_search(opt.subject.as_ref())?;
|
||||
let cwd = tab.cwd().to_search(&opt.subject)?;
|
||||
let hidden = tab.pref.show_hidden;
|
||||
|
||||
tab.search = Some(tokio::spawn(async move {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ impl Actor for Shell {
|
|||
|
||||
TasksProxy::open_shell_compat(ProcessOpenOpt {
|
||||
cwd: cwd.into(),
|
||||
cmd: Splatter::new(&selected).splat(opt.run.as_ref()),
|
||||
cmd: Splatter::new(&selected).splat(&*opt.run),
|
||||
args: selected,
|
||||
block: opt.block,
|
||||
orphan: opt.orphan,
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ impl Sixel {
|
|||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut buf = vec![];
|
||||
write!(buf, "{START}P0;1;8q\"1;1;{};{}", img.width(), img.height())?;
|
||||
write!(buf, "{START}P9;1q\"1;1;{};{}", img.width(), img.height())?;
|
||||
|
||||
// Palette
|
||||
for (i, c) in qo.palette.iter().enumerate() {
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ mod tests {
|
|||
.call(())
|
||||
.unwrap();
|
||||
|
||||
line.spans.iter().map(|s| s.content.as_ref()).collect()
|
||||
line.spans.iter().map(|s| &*s.content).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl UserData for Error {
|
|||
methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| {
|
||||
Ok(match me {
|
||||
Self::Io(_) | Self::Fs(_) | Self::Serde(_) => lua.create_string(me.to_string()),
|
||||
Self::Custom(s) => lua.create_string(s.as_ref()),
|
||||
Self::Custom(s) => lua.create_string(&**s),
|
||||
})
|
||||
});
|
||||
methods.add_meta_function(MetaMethod::Concat, |lua, (lhs, rhs): (Value, Value)| {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,97 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataMethods, Value};
|
||||
use yazi_shared::path::{PathBufDyn, PathLike};
|
||||
use mlua::{ExternalError, ExternalResult, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use yazi_shared::{path::{PathBufDyn, PathLike, StripPrefixError}, strand::{AsStrand, Strand, StrandCow}};
|
||||
|
||||
pub struct Path(pub PathBufDyn);
|
||||
use crate::cached_field;
|
||||
|
||||
pub type PathRef = UserDataRef<Path>;
|
||||
|
||||
pub struct Path {
|
||||
inner: PathBufDyn,
|
||||
|
||||
v_ext: Option<Value>,
|
||||
v_name: Option<Value>,
|
||||
v_parent: Option<Value>,
|
||||
v_stem: Option<Value>,
|
||||
}
|
||||
|
||||
impl Deref for Path {
|
||||
type Target = PathBufDyn;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl From<Path> for PathBufDyn {
|
||||
fn from(value: Path) -> Self { value.inner }
|
||||
}
|
||||
|
||||
impl AsStrand for Path {
|
||||
fn as_strand(&self) -> Strand<'_> { self.inner.as_strand() }
|
||||
}
|
||||
|
||||
impl AsStrand for &Path {
|
||||
fn as_strand(&self) -> Strand<'_> { self.inner.as_strand() }
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn new(urn: impl Into<PathBufDyn>) -> Self { Self(urn.into()) }
|
||||
pub fn new(path: impl Into<PathBufDyn>) -> Self {
|
||||
Self {
|
||||
inner: path.into(),
|
||||
|
||||
v_ext: None,
|
||||
v_name: None,
|
||||
v_parent: None,
|
||||
v_stem: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn ends_with(&self, child: Value) -> mlua::Result<bool> {
|
||||
match child {
|
||||
Value::String(s) => {
|
||||
self.try_ends_with(StrandCow::with(self.kind(), &*s.as_bytes())?).into_lua_err()
|
||||
}
|
||||
Value::UserData(ud) => self.try_ends_with(&*ud.borrow::<Self>()?).into_lua_err(),
|
||||
_ => Err("must be a string or Path".into_lua_err())?,
|
||||
}
|
||||
}
|
||||
|
||||
fn join(&self, other: Value) -> mlua::Result<Self> {
|
||||
Ok(Self::new(match other {
|
||||
Value::String(s) => {
|
||||
self.try_join(StrandCow::with(self.kind(), &*s.as_bytes())?).into_lua_err()?
|
||||
}
|
||||
Value::UserData(ref ud) => {
|
||||
let path = ud.borrow::<Self>()?;
|
||||
self.try_join(&*path).into_lua_err()?
|
||||
}
|
||||
_ => Err("must be a string or Path".into_lua_err())?,
|
||||
}))
|
||||
}
|
||||
|
||||
fn starts_with(&self, base: Value) -> mlua::Result<bool> {
|
||||
match base {
|
||||
Value::String(s) => {
|
||||
self.try_starts_with(StrandCow::with(self.kind(), &*s.as_bytes())?).into_lua_err()
|
||||
}
|
||||
Value::UserData(ud) => self.try_starts_with(&*ud.borrow::<Self>()?).into_lua_err(),
|
||||
_ => Err("must be a string or Path".into_lua_err())?,
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_prefix(&self, base: Value) -> mlua::Result<Option<Path>> {
|
||||
let strip = match base {
|
||||
Value::String(s) => self.try_strip_prefix(StrandCow::with(self.kind(), &*s.as_bytes())?),
|
||||
Value::UserData(ud) => self.try_strip_prefix(&*ud.borrow::<Self>()?),
|
||||
_ => Err("must be a string or Path".into_lua_err())?,
|
||||
};
|
||||
|
||||
Ok(match strip {
|
||||
Ok(p) => Some(Path::new(p)),
|
||||
Err(StripPrefixError::Exotic | StripPrefixError::NotPrefix) => None,
|
||||
Err(e @ StripPrefixError::WrongEncoding) => Err(e.into_lua_err())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for Path {
|
||||
|
|
@ -25,7 +104,29 @@ impl FromLua for Path {
|
|||
}
|
||||
|
||||
impl UserData for Path {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, ext, |lua, me| {
|
||||
me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose()
|
||||
});
|
||||
cached_field!(fields, name, |lua, me| {
|
||||
me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose()
|
||||
});
|
||||
cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new)));
|
||||
cached_field!(fields, stem, |lua, me| {
|
||||
me.stem().map(|s| lua.create_string(s.encoded_bytes())).transpose()
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("ends_with", |_, me, child: Value| me.ends_with(child));
|
||||
methods.add_method("join", |_, me, other: Value| me.join(other));
|
||||
methods.add_method("starts_with", |_, me, base: Value| me.starts_with(base));
|
||||
methods.add_method("strip_prefix", |_, me, base: Value| me.strip_prefix(base));
|
||||
|
||||
methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: mlua::String| {
|
||||
lua.create_string([lhs.encoded_bytes(), &rhs.as_bytes()].concat())
|
||||
});
|
||||
methods.add_meta_method(MetaMethod::Eq, |_, me, other: PathRef| Ok(me.inner == other.inner));
|
||||
methods
|
||||
.add_meta_method(MetaMethod::ToString, |lua, me, ()| lua.create_string(me.encoded_bytes()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use yazi_fs::{FsHash64, FsHash128};
|
||||
use yazi_shared::{path::StripPrefixError, strand::StrandCow, url::{AsUrl, UrlCow, UrlLike}};
|
||||
use yazi_shared::{path::StripPrefixError, scheme::SchemeCow, strand::{StrandLike, ToStrand}, url::{AsUrl, UrlCow, UrlLike}};
|
||||
|
||||
use crate::{Scheme, cached_field, deprecate};
|
||||
use crate::{Path, Scheme, cached_field, deprecate};
|
||||
|
||||
pub type UrlRef = UserDataRef<Url>;
|
||||
|
||||
pub struct Url {
|
||||
inner: yazi_shared::url::UrlBuf,
|
||||
|
||||
v_path: Option<Value>,
|
||||
v_name: Option<Value>,
|
||||
v_stem: Option<Value>,
|
||||
v_ext: Option<Value>,
|
||||
|
|
@ -61,6 +62,7 @@ impl Url {
|
|||
Self {
|
||||
inner: url.into(),
|
||||
|
||||
v_path: None,
|
||||
v_name: None,
|
||||
v_stem: None,
|
||||
v_ext: None,
|
||||
|
|
@ -78,13 +80,74 @@ impl Url {
|
|||
"Url",
|
||||
lua.create_function(|_, value: Value| {
|
||||
Ok(match value {
|
||||
Value::String(s) => Self::try_from(s.as_bytes().as_ref())?,
|
||||
Value::UserData(ud) => Self::new(ud.borrow::<Self>()?.inner.clone()),
|
||||
Value::String(s) => Self::try_from(&*s.as_bytes())?,
|
||||
Value::UserData(ud) => Self::new(&ud.borrow::<Self>()?.inner),
|
||||
_ => Err("Expected a string or a Url".into_lua_err())?,
|
||||
})
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
||||
fn ends_with(&self, child: Value) -> mlua::Result<bool> {
|
||||
match child {
|
||||
Value::String(s) => self.try_ends_with(UrlCow::try_from(&*s.as_bytes())?).into_lua_err(),
|
||||
Value::UserData(ud) => self.try_ends_with(&*ud.borrow::<Self>()?).into_lua_err(),
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(&self, long: Option<bool>) -> mlua::Result<String> {
|
||||
Ok(if long.unwrap_or(false) {
|
||||
format!("{:x}", self.hash_u128())
|
||||
} else {
|
||||
format!("{:x}", self.hash_u64())
|
||||
})
|
||||
}
|
||||
|
||||
fn join(&self, lua: &Lua, other: Value) -> mlua::Result<Value> {
|
||||
match other {
|
||||
Value::String(s) => {
|
||||
let b = s.as_bytes();
|
||||
let (scheme, path) = SchemeCow::parse(&*b)?;
|
||||
if scheme == self.scheme() {
|
||||
Self::new(self.try_join(path).into_lua_err()?).into_lua(lua)
|
||||
} else {
|
||||
Self::new(UrlCow::try_from((scheme, path))?).into_lua(lua)
|
||||
}
|
||||
}
|
||||
Value::UserData(ref ud) => {
|
||||
let url = ud.borrow::<Self>()?;
|
||||
if url.scheme() == self.scheme() {
|
||||
Self::new(self.try_join(url.loc()).into_lua_err()?).into_lua(lua)
|
||||
} else {
|
||||
Ok(other)
|
||||
}
|
||||
}
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
}
|
||||
}
|
||||
|
||||
fn starts_with(&self, base: Value) -> mlua::Result<bool> {
|
||||
match base {
|
||||
Value::String(s) => self.try_starts_with(UrlCow::try_from(&*s.as_bytes())?).into_lua_err(),
|
||||
Value::UserData(ud) => self.try_starts_with(&*ud.borrow::<Self>()?).into_lua_err(),
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_prefix(&self, base: Value) -> mlua::Result<Option<Path>> {
|
||||
let strip = match base {
|
||||
Value::String(s) => self.try_strip_prefix(UrlCow::try_from(&*s.as_bytes())?),
|
||||
Value::UserData(ud) => self.try_strip_prefix(&*ud.borrow::<Self>()?),
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
};
|
||||
|
||||
Ok(match strip {
|
||||
Ok(p) => Some(Path::new(p)),
|
||||
Err(StripPrefixError::Exotic | StripPrefixError::NotPrefix) => None,
|
||||
Err(e @ StripPrefixError::WrongEncoding) => Err(e.into_lua_err())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for Url {
|
||||
|
|
@ -98,6 +161,7 @@ impl FromLua for Url {
|
|||
|
||||
impl UserData for Url {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, path, |_, me| Ok(Path::new(me.loc())));
|
||||
cached_field!(fields, name, |lua, me| {
|
||||
me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose()
|
||||
});
|
||||
|
|
@ -108,7 +172,7 @@ impl UserData for Url {
|
|||
me.ext().map(|s| lua.create_string(s.encoded_bytes())).transpose()
|
||||
});
|
||||
cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new)));
|
||||
cached_field!(fields, urn, |_, me| Ok(super::Path::new(me.urn())));
|
||||
cached_field!(fields, urn, |_, me| Ok(Path::new(me.urn())));
|
||||
cached_field!(fields, base, |_, me| {
|
||||
Ok(Some(me.base()).filter(|u| !u.loc().is_empty()).map(Self::new))
|
||||
});
|
||||
|
|
@ -131,65 +195,11 @@ 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) => {
|
||||
let b = s.as_bytes();
|
||||
me.try_join(StrandCow::with(me.kind(), &*b).into_lua_err()?).into_lua_err()?
|
||||
}
|
||||
Value::UserData(ud) => {
|
||||
let url = ud.borrow::<Self>()?;
|
||||
if !me.scheme().covariant(url.scheme()) {
|
||||
return Err("cannot join Urls with different schemes".into_lua_err());
|
||||
}
|
||||
me.try_join(url.loc()).into_lua_err()?
|
||||
}
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
}))
|
||||
});
|
||||
methods.add_method("starts_with", |_, me, base: Value| {
|
||||
Ok(match base {
|
||||
Value::String(s) => {
|
||||
let b = s.as_bytes();
|
||||
me.loc().try_starts_with(&StrandCow::with(me.kind(), &*b).into_lua_err()?).into_lua_err()
|
||||
}
|
||||
Value::UserData(ud) => me.try_starts_with(&*ud.borrow::<Self>()?).into_lua_err(),
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
})
|
||||
});
|
||||
methods.add_method("ends_with", |_, me, child: Value| {
|
||||
Ok(match child {
|
||||
Value::String(s) => {
|
||||
let b = s.as_bytes();
|
||||
me.loc().try_ends_with(&StrandCow::with(me.kind(), &*b).into_lua_err()?).into_lua_err()
|
||||
}
|
||||
Value::UserData(ud) => me.try_ends_with(&*ud.borrow::<Self>()?).into_lua_err(),
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
})
|
||||
});
|
||||
methods.add_method("strip_prefix", |_, me, base: Value| {
|
||||
let strip = match base {
|
||||
Value::String(s) => {
|
||||
let b = s.as_bytes();
|
||||
me.loc().try_strip_prefix(&StrandCow::with(me.kind(), &*b).into_lua_err()?)
|
||||
}
|
||||
Value::UserData(ud) => me.try_strip_prefix(&*ud.borrow::<Self>()?),
|
||||
_ => Err("must be a string or Url".into_lua_err())?,
|
||||
};
|
||||
|
||||
Ok(match strip {
|
||||
Ok(p) => Some(Self::new(p)), // TODO: return `Path` instead of `Url`
|
||||
Err(StripPrefixError::Exotic | StripPrefixError::NotPrefix) => None,
|
||||
Err(e @ StripPrefixError::WrongEncoding) => Err(e.into_lua_err())?,
|
||||
})
|
||||
});
|
||||
methods.add_method("ends_with", |_, me, child: Value| me.ends_with(child));
|
||||
methods.add_method("hash", |_, me, long: Option<bool>| me.hash(long));
|
||||
methods.add_method("join", |lua, me, other: Value| me.join(lua, other));
|
||||
methods.add_method("starts_with", |_, me, base: Value| me.starts_with(base));
|
||||
methods.add_method("strip_prefix", |_, me, base: Value| me.strip_prefix(base));
|
||||
|
||||
methods.add_function_mut("into_search", |_, (ud, domain): (AnyUserData, mlua::String)| {
|
||||
let url = ud.take::<Self>()?.inner.into_search(domain.to_str()?).into_lua_err()?;
|
||||
|
|
@ -198,10 +208,10 @@ impl UserData for Url {
|
|||
|
||||
methods.add_meta_method(MetaMethod::Eq, |_, me, other: UrlRef| Ok(me.inner == other.inner));
|
||||
methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| {
|
||||
lua.create_string(me.os_str().as_encoded_bytes())
|
||||
lua.create_string(me.to_strand().encoded_bytes())
|
||||
});
|
||||
methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: mlua::String| {
|
||||
lua.create_string([lhs.os_str().as_encoded_bytes(), &rhs.as_bytes()].concat())
|
||||
lua.create_string([lhs.to_strand().encoded_bytes(), &rhs.as_bytes()].concat())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
# External dependencies
|
||||
syn = { version = "2.0.110", features = [ "full" ] }
|
||||
syn = { version = "2.0.111", features = [ "full" ] }
|
||||
quote = "1.0.42"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}};
|
||||
use yazi_shared::{IntoStringLossy, url::UrlBuf};
|
||||
use yazi_shared::{strand::ToStrand, url::UrlBuf};
|
||||
|
||||
use super::{Offset, Position};
|
||||
use crate::YAZI;
|
||||
|
|
@ -123,7 +123,7 @@ impl ConfirmCfg {
|
|||
Self::replace_number(&YAZI.confirm.trash_title, urls.len()),
|
||||
YAZI.confirm.trash_position(),
|
||||
None,
|
||||
Self::truncate_list(urls.iter(), urls.len(), 100),
|
||||
Self::truncate_list(urls, urls.len(), 100),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ impl ConfirmCfg {
|
|||
Self::replace_number(&YAZI.confirm.delete_title, urls.len()),
|
||||
YAZI.confirm.delete_position(),
|
||||
None,
|
||||
Self::truncate_list(urls.iter(), urls.len(), 100),
|
||||
Self::truncate_list(urls, urls.len(), 100),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ impl ConfirmCfg {
|
|||
YAZI.confirm.overwrite_title.clone(),
|
||||
YAZI.confirm.overwrite_position(),
|
||||
Some(Text::raw(&YAZI.confirm.overwrite_body)),
|
||||
Some(url.into_string_lossy().into()),
|
||||
Some(url.to_strand().into_string_lossy().into()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ impl ConfirmCfg {
|
|||
Self::replace_number(&YAZI.confirm.quit_title, len),
|
||||
YAZI.confirm.quit_position(),
|
||||
Some(Text::raw(&YAZI.confirm.quit_body)),
|
||||
Self::truncate_list(names.into_iter(), len, 10),
|
||||
Self::truncate_list(names, len, 10),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -158,18 +158,18 @@ impl ConfirmCfg {
|
|||
tpl.replace("{n}", &n.to_string()).replace("{s}", if n > 1 { "s" } else { "" })
|
||||
}
|
||||
|
||||
fn truncate_list(
|
||||
it: impl Iterator<Item = impl IntoStringLossy>,
|
||||
len: usize,
|
||||
max: usize,
|
||||
) -> Option<Text<'static>> {
|
||||
fn truncate_list<I>(it: I, len: usize, max: usize) -> Option<Text<'static>>
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: ToStrand,
|
||||
{
|
||||
let mut lines = Vec::with_capacity(len.min(max + 1));
|
||||
for (i, s) in it.enumerate() {
|
||||
for (i, s) in it.into_iter().enumerate() {
|
||||
if i >= max {
|
||||
lines.push(format!("... and {} more", len - max));
|
||||
break;
|
||||
}
|
||||
lines.push(s.into_string_lossy());
|
||||
lines.push(s.to_strand().into_string_lossy());
|
||||
}
|
||||
Some(Text::from_iter(lines))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ impl Sendable {
|
|||
Data::Url(ud.take::<yazi_binding::Url>()?.into())
|
||||
}
|
||||
Some(t) if t == TypeId::of::<yazi_binding::Path>() => {
|
||||
Data::Path(ud.take::<yazi_binding::Path>()?.0)
|
||||
Data::Path(ud.take::<yazi_binding::Path>()?.into())
|
||||
}
|
||||
Some(t) if t == TypeId::of::<yazi_binding::Id>() => {
|
||||
Data::Id(**ud.borrow::<yazi_binding::Id>()?)
|
||||
|
|
@ -103,7 +103,7 @@ impl Sendable {
|
|||
Data::Boolean(b) => Value::Boolean(*b),
|
||||
Data::Integer(i) => Value::Integer(*i),
|
||||
Data::Number(n) => Value::Number(*n),
|
||||
Data::String(s) => Value::String(lua.create_string(s.as_ref())?),
|
||||
Data::String(s) => Value::String(lua.create_string(&**s)?),
|
||||
Data::List(l) => {
|
||||
let mut vec = Vec::with_capacity(l.len());
|
||||
for v in l {
|
||||
|
|
@ -175,7 +175,7 @@ impl Sendable {
|
|||
match k {
|
||||
DataKey::Integer(i) => tbl.raw_set(i + 1, Self::data_to_value_ref(lua, v)?),
|
||||
DataKey::String(s) => {
|
||||
tbl.raw_set(replace_cow(s.as_ref(), "-", "_"), Self::data_to_value_ref(lua, v)?)
|
||||
tbl.raw_set(replace_cow(&**s, "-", "_"), Self::data_to_value_ref(lua, v)?)
|
||||
}
|
||||
_ => Err("invalid key in Data".into_lua_err()),
|
||||
}?;
|
||||
|
|
@ -215,7 +215,7 @@ impl Sendable {
|
|||
DataKey::Url(ud.take::<yazi_binding::Url>()?.into())
|
||||
}
|
||||
Some(t) if t == TypeId::of::<yazi_binding::Path>() => {
|
||||
DataKey::Path(ud.take::<yazi_binding::Path>()?.0)
|
||||
DataKey::Path(ud.take::<yazi_binding::Path>()?.into())
|
||||
}
|
||||
Some(t) if t == TypeId::of::<yazi_binding::Id>() => {
|
||||
DataKey::Id(**ud.borrow::<yazi_binding::Id>()?)
|
||||
|
|
@ -241,7 +241,7 @@ impl Sendable {
|
|||
DataKey::Boolean(b) => Value::Boolean(*b),
|
||||
DataKey::Integer(i) => Value::Integer(*i),
|
||||
DataKey::Number(n) => Value::Number(n.0),
|
||||
DataKey::String(s) => Value::String(lua.create_string(s.as_ref())?),
|
||||
DataKey::String(s) => Value::String(lua.create_string(&**s)?),
|
||||
DataKey::Id(i) => yazi_binding::Id(*i).into_lua(lua)?,
|
||||
DataKey::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?,
|
||||
DataKey::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use crate::app::App;
|
|||
impl App {
|
||||
pub(crate) fn plugin(&mut self, mut opt: PluginOpt) -> Result<Data> {
|
||||
let mut hits = false;
|
||||
if let Some(chunk) = LOADER.read().get(opt.id.as_ref()) {
|
||||
if let Some(chunk) = LOADER.read().get(&*opt.id) {
|
||||
hits = true;
|
||||
opt.mode = opt.mode.auto_then(chunk.sync_entry);
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ impl App {
|
|||
|
||||
pub(crate) fn plugin_do(&mut self, opt: PluginOpt) -> Result<Data> {
|
||||
let loader = LOADER.read();
|
||||
let Some(chunk) = loader.get(opt.id.as_ref()) else {
|
||||
let Some(chunk) = loader.get(&*opt.id) else {
|
||||
succ!(warn!("plugin `{}` not found", opt.id));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use yazi_boot::ARGS;
|
||||
use yazi_fs::provider::{Provider, local::Local};
|
||||
use yazi_shared::{event::EventQuit, url::UrlLike};
|
||||
use yazi_shared::{event::EventQuit, strand::{StrandBuf, StrandLike, ToStrand}};
|
||||
|
||||
use crate::{Term, app::App};
|
||||
|
||||
|
|
@ -25,14 +23,14 @@ impl App {
|
|||
|
||||
async fn cwd_to_file(&self, no: bool) {
|
||||
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no) {
|
||||
let cwd = self.core.mgr.cwd().os_str();
|
||||
Local::regular(p).write(cwd.as_encoded_bytes()).await.ok();
|
||||
let cwd = self.core.mgr.cwd().to_strand();
|
||||
Local::regular(p).write(cwd.encoded_bytes()).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
async fn selected_to_file(&self, selected: Option<OsString>) {
|
||||
async fn selected_to_file(&self, selected: Option<StrandBuf>) {
|
||||
if let (Some(s), Some(p)) = (selected, &ARGS.chooser_file) {
|
||||
Local::regular(p).write(s.as_encoded_bytes()).await.ok();
|
||||
Local::regular(p).write(s.encoded_bytes()).await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ foldhash = { workspace = true }
|
|||
hashbrown = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
percent-encoding = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
scopeguard = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use yazi_shared::{loc::LocBuf, path::PathDyn, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_shared::{path::{PathBufDyn, PathDyn}, url::{UrlBuf, UrlCow, UrlLike}};
|
||||
|
||||
pub fn clean_url<'a>(url: impl Into<UrlCow<'a>>) -> UrlBuf {
|
||||
let cow: UrlCow = url.into();
|
||||
|
|
@ -10,25 +8,12 @@ pub fn clean_url<'a>(url: impl Into<UrlCow<'a>>) -> UrlBuf {
|
|||
cow.trail().components().count() - 1,
|
||||
);
|
||||
|
||||
match cow.as_url() {
|
||||
Url::Regular(_) => UrlBuf::Regular(path.into()),
|
||||
Url::Search { domain, .. } => UrlBuf::Search {
|
||||
loc: LocBuf::<PathBuf>::with(path, uri, urn).expect("create Loc from cleaned path"),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
Url::Archive { domain, .. } => UrlBuf::Archive {
|
||||
loc: LocBuf::<PathBuf>::with(path, uri, urn).expect("create Loc from cleaned path"),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
Url::Sftp { domain, .. } => UrlBuf::Sftp {
|
||||
loc: LocBuf::<PathBuf>::with(path, uri, urn).expect("create Loc from cleaned path"),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
}
|
||||
let scheme = cow.scheme().to_owned().with_ports(uri, urn);
|
||||
(scheme, path).try_into().expect("UrlBuf from cleaned path")
|
||||
}
|
||||
|
||||
fn clean_path_impl(path: PathDyn, base: usize, trail: usize) -> (PathBuf, usize, usize) {
|
||||
use std::path::Component::*;
|
||||
fn clean_path_impl(path: PathDyn, base: usize, trail: usize) -> (PathBufDyn, usize, usize) {
|
||||
use yazi_shared::path::Component::*;
|
||||
|
||||
let mut out = vec![];
|
||||
let mut uri_count = 0;
|
||||
|
|
@ -58,7 +43,14 @@ fn clean_path_impl(path: PathDyn, base: usize, trail: usize) -> (PathBuf, usize,
|
|||
}
|
||||
}
|
||||
|
||||
(if out.is_empty() { PathBuf::from(".") } else { out.iter().collect() }, uri_count, urn_count)
|
||||
let kind = path.kind();
|
||||
let path = if out.is_empty() {
|
||||
PathBufDyn::with_str(kind, ".")
|
||||
} else {
|
||||
PathBufDyn::from_components(kind, out).expect("components with same kind")
|
||||
};
|
||||
|
||||
(path, uri_count, urn_count)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, path::PathBuf};
|
||||
|
||||
use yazi_shared::{FromWtf8Vec, loc::LocBuf, path::{PathBufDyn, PathCow, PathDyn, PathLike}, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_shared::{FromWtf8Vec, loc::LocBuf, path::{PathBufDyn, PathCow, PathDyn, PathKind, PathLike}, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
|
||||
|
||||
use crate::{CWD, path::clean_url};
|
||||
|
||||
|
|
@ -60,11 +60,12 @@ fn expand_variables<'a>(p: PathDyn<'a>) -> PathCow<'a> {
|
|||
.map_or_else(|| caps.get(0).unwrap().as_bytes().to_owned(), |s| s.into_encoded_bytes())
|
||||
});
|
||||
|
||||
match (b, p) {
|
||||
match (b, p.kind()) {
|
||||
(Cow::Borrowed(_), _) => p.into(),
|
||||
(Cow::Owned(b), PathDyn::Os(_)) => {
|
||||
(Cow::Owned(b), PathKind::Os) => {
|
||||
PathBufDyn::Os(std::path::PathBuf::from_wtf8_vec(b).expect("valid WTF-8 path")).into()
|
||||
}
|
||||
(Cow::Owned(b), PathKind::Unix) => PathBufDyn::Unix(b.into()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
use std::{borrow::Cow, ffi::OsStr};
|
||||
use yazi_shared::{strand::StrandCow, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
|
||||
pub fn skip_url(url: &UrlBuf, n: usize) -> Cow<'_, OsStr> {
|
||||
pub fn skip_url(url: &UrlBuf, n: usize) -> StrandCow<'_> {
|
||||
let mut it = url.components();
|
||||
for _ in 0..n {
|
||||
if it.next().is_none() {
|
||||
return OsStr::new("").into();
|
||||
return StrandCow::default();
|
||||
}
|
||||
}
|
||||
it.os_str()
|
||||
it.strand()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -32,12 +32,14 @@ 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,5 +1,3 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use yazi_shared::{path::PathBufDyn, url::{UrlCow, UrlLike}};
|
||||
|
||||
|
|
@ -23,7 +21,10 @@ fn url_relative_to_<'a>(from: UrlCow<'_>, to: UrlCow<'a>) -> Result<UrlCow<'a>>
|
|||
}
|
||||
|
||||
if from.covariant(&to) {
|
||||
return UrlCow::try_from((to.scheme().zeroed().to_owned(), PathBufDyn::with(to.kind(), ".")?));
|
||||
return UrlCow::try_from((
|
||||
to.scheme().zeroed().to_owned(),
|
||||
PathBufDyn::with_str(to.kind(), "."),
|
||||
));
|
||||
}
|
||||
|
||||
let (mut f_it, mut t_it) = (from.components(), to.components());
|
||||
|
|
@ -44,7 +45,8 @@ fn url_relative_to_<'a>(from: UrlCow<'_>, to: UrlCow<'a>) -> Result<UrlCow<'a>>
|
|||
let dots = f_head.into_iter().chain(f_it).map(|_| ParentDir);
|
||||
let rest = t_head.into_iter().chain(t_it);
|
||||
|
||||
let buf: PathBuf = dots.chain(rest).collect(); // FIXME: remove PathBuf
|
||||
let buf = PathBufDyn::from(buf);
|
||||
let iter = dots.chain(rest).map(|c| c.downgrade().expect("path component from dot or normal"));
|
||||
let buf = PathBufDyn::from_components(to.kind(), iter)?;
|
||||
|
||||
UrlCow::try_from((to.scheme().zeroed().to_owned(), buf))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use yazi_shared::{path::{AsPath, PathBufDyn}, scheme::SchemeKind, url::{Url, Url
|
|||
|
||||
use crate::{cha::Cha, path::absolute_url, provider::{Attrs, Provider}};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
pub struct Local<'a> {
|
||||
url: Url<'a>,
|
||||
path: &'a Path,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use yazi_shared::{LcgRng, natsort, path::PathBufDyn, translit::Transliterator, url::UrlLike};
|
||||
use rand::{RngCore, SeedableRng, rngs::SmallRng};
|
||||
use yazi_shared::{natsort, path::PathBufDyn, translit::Transliterator, url::UrlLike};
|
||||
|
||||
use crate::{File, SortBy};
|
||||
|
||||
|
|
@ -59,8 +60,8 @@ impl FilesSorter {
|
|||
if ord == Ordering::Equal { by_alphabetical(a, b) } else { ord }
|
||||
}),
|
||||
SortBy::Random => {
|
||||
let mut rng = LcgRng::default();
|
||||
items.sort_unstable_by(|a, b| self.cmp(rng.next(), rng.next(), self.promote(a, b)))
|
||||
let mut rng = SmallRng::from_os_rng();
|
||||
items.sort_unstable_by(|a, b| self.cmp(rng.next_u64(), rng.next_u64(), self.promote(a, b)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
|||
|
||||
use yazi_shared::{path::PathDyn, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
||||
|
||||
use crate::{FsHash128, FsScheme, path::PercentEncoding};
|
||||
use crate::{FsHash128, FsScheme};
|
||||
|
||||
pub trait FsUrl<'a> {
|
||||
fn cache(&self) -> Option<PathBuf>;
|
||||
|
|
@ -26,12 +26,13 @@ impl<'a> FsUrl<'a> for Url<'a> {
|
|||
fn cache(&self) -> Option<PathBuf> {
|
||||
fn with_loc(loc: PathDyn, mut root: PathBuf) -> PathBuf {
|
||||
let mut it = loc.components();
|
||||
if it.next() == Some(std::path::Component::RootDir) {
|
||||
root.push(it.as_path().percent_encode());
|
||||
} else {
|
||||
root.push(".%2F");
|
||||
root.push(loc.percent_encode());
|
||||
}
|
||||
todo!();
|
||||
// if it.next() == Some(std::path::Component::RootDir) {
|
||||
// root.push(it.as_path().percent_encode());
|
||||
// } else {
|
||||
// root.push(".%2F");
|
||||
// root.push(loc.percent_encode());
|
||||
// }
|
||||
root
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ impl FilesOp {
|
|||
url.into(),
|
||||
sizes
|
||||
.pairs::<Path, u64>()
|
||||
.map(|r| r.map(|(urn, size)| (urn.0, size)))
|
||||
.map(|r| r.map(|(urn, size)| (urn.into(), size)))
|
||||
.collect::<mlua::Result<_>>()?,
|
||||
)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Symbol<str>, skip: us
|
|||
plugin.call_method("peek", job)
|
||||
});
|
||||
|
||||
AppProxy::plugin(PluginOpt::new_callback(cmd.name.as_ref(), cb));
|
||||
AppProxy::plugin(PluginOpt::new_callback(&*cmd.name, cb));
|
||||
}
|
||||
|
||||
fn peek_async(
|
||||
|
|
|
|||
|
|
@ -16,5 +16,5 @@ pub fn seek_sync(cmd: &'static Cmd, file: yazi_fs::File, units: i16) {
|
|||
plugin.call_method("seek", job)
|
||||
});
|
||||
|
||||
AppProxy::plugin(PluginOpt::new_callback(cmd.name.as_ref(), cb));
|
||||
AppProxy::plugin(PluginOpt::new_callback(&*cmd.name, cb));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use std::{any::TypeId, io, process::Stdio};
|
||||
use std::{any::TypeId, ffi::OsStr, io, process::Stdio};
|
||||
|
||||
use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, MetaMethod, Table, UserData, Value};
|
||||
use tokio::process::{ChildStderr, ChildStdin, ChildStdout};
|
||||
use yazi_binding::Error;
|
||||
use yazi_shared::IntoOsStr;
|
||||
use yazi_shared::FromWtf8;
|
||||
|
||||
use super::{Child, output::Output};
|
||||
use crate::process::Status;
|
||||
|
|
@ -144,11 +144,11 @@ impl UserData for Command {
|
|||
let mut me = ud.borrow_mut::<Self>()?;
|
||||
match arg {
|
||||
Value::String(s) => {
|
||||
me.inner.arg(s.as_bytes().into_os_str()?);
|
||||
me.inner.arg(OsStr::from_wtf8(&*s.as_bytes())?);
|
||||
}
|
||||
Value::Table(t) => {
|
||||
for s in t.sequence_values::<mlua::String>() {
|
||||
me.inner.arg(s?.as_bytes().into_os_str()?);
|
||||
me.inner.arg(OsStr::from_wtf8(&*s?.as_bytes())?);
|
||||
}
|
||||
}
|
||||
_ => return Err("arg must be a string or table of strings".into_lua_err()),
|
||||
|
|
@ -165,7 +165,7 @@ impl UserData for Command {
|
|||
|_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| {
|
||||
ud.borrow_mut::<Self>()?
|
||||
.inner
|
||||
.env(key.as_bytes().into_os_str()?, value.as_bytes().into_os_str()?);
|
||||
.env(OsStr::from_wtf8(&*key.as_bytes())?, OsStr::from_wtf8(&*value.as_bytes())?);
|
||||
Ok(ud)
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl Fetcher {
|
|||
|
||||
impl UserData for Fetcher {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&*me.inner.run.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ impl Spotter {
|
|||
|
||||
impl UserData for Spotter {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&*me.inner.run.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ impl Preloader {
|
|||
|
||||
impl UserData for Preloader {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&*me.inner.run.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +107,6 @@ impl Previewer {
|
|||
|
||||
impl UserData for Previewer {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&*me.inner.run.name));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ impl Utils {
|
|||
Some(false) => yazi_shared::shell::escape_windows(s.as_ref()),
|
||||
None => yazi_shared::shell::escape_native(s.as_ref()),
|
||||
};
|
||||
lua.create_string(s.as_ref())
|
||||
lua.create_string(&*s)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ percent-encoding = { workspace = true }
|
|||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
typed-path = { workspace = true }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
libc = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub enum Data {
|
|||
Id(Id),
|
||||
#[serde(skip_deserializing)]
|
||||
Url(UrlBuf),
|
||||
#[serde(skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
Path(PathBufDyn),
|
||||
#[serde(skip)]
|
||||
Bytes(Vec<u8>),
|
||||
|
|
@ -150,7 +150,7 @@ impl<'a> TryFrom<&'a Data> for Cow<'a, str> {
|
|||
|
||||
fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Data::String(s) => Ok(s.as_ref().into()),
|
||||
Data::String(s) => Ok(Cow::Borrowed(s)),
|
||||
_ => bail!("not a string"),
|
||||
}
|
||||
}
|
||||
|
|
@ -193,7 +193,7 @@ impl<'a> TryFrom<&'a Data> for UrlCow<'a> {
|
|||
|
||||
fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Data::String(s) => s.as_ref().try_into(),
|
||||
Data::String(s) => Self::try_from(&**s),
|
||||
Data::Url(u) => Ok(u.into()),
|
||||
Data::Bytes(b) => b.as_slice().try_into(),
|
||||
_ => bail!("not a URL"),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub enum DataKey {
|
|||
Id(Id),
|
||||
#[serde(skip_deserializing)]
|
||||
Url(UrlBuf),
|
||||
#[serde(skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
Path(PathBufDyn),
|
||||
#[serde(skip)]
|
||||
Bytes(Vec<u8>),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use crossterm::event::{KeyEvent, MouseEvent};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use super::CmdCow;
|
||||
use crate::RoCell;
|
||||
use crate::{RoCell, strand::StrandBuf};
|
||||
|
||||
static TX: RoCell<mpsc::UnboundedSender<Event>> = RoCell::new();
|
||||
static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new();
|
||||
|
|
@ -25,7 +23,7 @@ pub enum Event {
|
|||
pub struct EventQuit {
|
||||
pub code: i32,
|
||||
pub no_cwd_file: bool,
|
||||
pub selected: Option<OsString>,
|
||||
pub selected: Option<StrandBuf>,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
yazi_macro::mod_pub!(data errors event loc path pool scheme shell strand translit url);
|
||||
|
||||
yazi_macro::mod_flat!(alias bytes chars condition debounce either env id layer natsort os osstr predictor rand ro_cell source string 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 wtf8);
|
||||
|
||||
pub fn init() {
|
||||
pool::init();
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}};
|
||||
|
||||
pub trait IntoOsStr<'a> {
|
||||
type Error;
|
||||
|
||||
fn into_os_str(self) -> Result<Cow<'a, OsStr>, Self::Error>;
|
||||
}
|
||||
|
||||
impl<'a> IntoOsStr<'a> for Cow<'a, [u8]> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn into_os_str(self) -> Result<Cow<'a, OsStr>, Self::Error> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::{OsStrExt, OsStringExt};
|
||||
Ok(match self {
|
||||
Cow::Borrowed(b) => Cow::Borrowed(OsStr::from_bytes(b)),
|
||||
Cow::Owned(b) => Cow::Owned(OsString::from_vec(b)),
|
||||
})
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
Ok(match self {
|
||||
Cow::Borrowed(b) => Cow::Borrowed(OsStr::new(str::from_utf8(b)?)),
|
||||
Cow::Owned(b) => Cow::Owned(OsString::from(String::from_utf8(b)?)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoOsStr<'a> for &'a [u8] {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn into_os_str(self) -> Result<Cow<'a, OsStr>, Self::Error> { Cow::Borrowed(self).into_os_str() }
|
||||
}
|
||||
|
||||
// --- OsStrJoin
|
||||
pub trait OsStrJoin {
|
||||
fn join(&self, sep: &OsStr) -> OsString;
|
||||
}
|
||||
|
||||
impl<T> OsStrJoin for Vec<T>
|
||||
where
|
||||
T: AsRef<OsStr>,
|
||||
{
|
||||
fn join(&self, sep: &OsStr) -> OsString {
|
||||
if self.is_empty() {
|
||||
return OsString::new();
|
||||
}
|
||||
|
||||
let mut result = OsString::new();
|
||||
for (i, item) in self.iter().enumerate() {
|
||||
if i > 0 {
|
||||
result.push(sep);
|
||||
}
|
||||
result.push(item.as_ref());
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
use std::ffi::{OsStr, OsString};
|
||||
|
||||
use hashbrown::Equivalent;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{FromWtf8, path::{AsPath, PathBufDynError, PathDyn, PathKind, SetNameError}, strand::{AsStrand, Strand, StrandError}};
|
||||
use crate::{FromWtf8, FromWtf8Vec, path::{AsPath, Component, PathDyn, PathDynError, PathKind, SetNameError}, strand::{AsStrand, Strand}};
|
||||
|
||||
// --- PathBufDyn
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum PathBufDyn {
|
||||
Os(std::path::PathBuf),
|
||||
Unix(typed_path::UnixPathBuf),
|
||||
}
|
||||
|
||||
impl From<std::path::PathBuf> for PathBufDyn {
|
||||
|
|
@ -21,7 +20,7 @@ impl From<PathDyn<'_>> for PathBufDyn {
|
|||
}
|
||||
|
||||
impl TryFrom<PathBufDyn> for std::path::PathBuf {
|
||||
type Error = PathBufDynError;
|
||||
type Error = PathDynError;
|
||||
|
||||
fn try_from(value: PathBufDyn) -> Result<Self, Self::Error> { value.into_os() }
|
||||
}
|
||||
|
|
@ -46,19 +45,34 @@ impl PathBufDyn {
|
|||
{
|
||||
match kind.into() {
|
||||
PathKind::Os => Self::Os(unsafe { OsString::from_encoded_bytes_unchecked(bytes) }.into()),
|
||||
PathKind::Unix => Self::Unix(bytes.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_components<'a, K, I>(kind: K, iter: I) -> Result<Self, PathDynError>
|
||||
where
|
||||
K: Into<PathKind>,
|
||||
I: IntoIterator<Item = Component<'a>>,
|
||||
{
|
||||
Ok(match kind.into() {
|
||||
PathKind::Os => Self::Os(iter.into_iter().collect::<Result<_, PathDynError>>()?),
|
||||
PathKind::Unix => Self::Unix(iter.into_iter().collect::<Result<_, PathDynError>>()?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_encoded_bytes(self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Os(p) => p.into_os_string().into_encoded_bytes(),
|
||||
Self::Unix(p) => p.into_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_os(self) -> Result<std::path::PathBuf, PathBufDynError> {
|
||||
pub fn into_os(self) -> Result<std::path::PathBuf, PathDynError> {
|
||||
Ok(match self {
|
||||
Self::Os(p) => p,
|
||||
Self::Unix(_) => Err(PathDynError::AsOs)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -66,13 +80,11 @@ impl PathBufDyn {
|
|||
pub fn new(kind: PathKind) -> Self {
|
||||
match kind {
|
||||
PathKind::Os => Self::Os(std::path::PathBuf::new()),
|
||||
PathKind::Unix => Self::Unix(typed_path::UnixPathBuf::new()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn os(path: impl Into<std::path::PathBuf>) -> Self { Self::Os(path.into()) }
|
||||
|
||||
pub fn try_extend<T>(&mut self, paths: T) -> Result<(), StrandError>
|
||||
pub fn try_extend<T>(&mut self, paths: T) -> Result<(), PathDynError>
|
||||
where
|
||||
T: IntoIterator,
|
||||
T::Item: AsPath,
|
||||
|
|
@ -83,13 +95,14 @@ impl PathBufDyn {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn try_push<T>(&mut self, path: T) -> Result<(), StrandError>
|
||||
pub fn try_push<T>(&mut self, path: T) -> Result<(), PathDynError>
|
||||
where
|
||||
T: AsPath,
|
||||
{
|
||||
let path = path.as_path();
|
||||
Ok(match self {
|
||||
Self::Os(p) => p.push(path.as_os()?),
|
||||
Self::Unix(p) => p.push(path.encoded_bytes()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -98,22 +111,25 @@ impl PathBufDyn {
|
|||
T: AsStrand,
|
||||
{
|
||||
Ok(match (self, name.as_strand()) {
|
||||
(Self::Os(p), Strand::Os(q)) => p.set_file_name(q),
|
||||
(Self::Os(p), Strand::Utf8(q)) => p.set_file_name(q),
|
||||
(Self::Os(p), Strand::Os(s)) => p.set_file_name(s),
|
||||
(Self::Os(p), Strand::Utf8(s)) => p.set_file_name(s),
|
||||
(Self::Os(p), Strand::Bytes(b)) => {
|
||||
p.set_file_name(OsStr::from_wtf8(b).map_err(|_| SetNameError::FromWtf8)?)
|
||||
}
|
||||
|
||||
(Self::Unix(p), s) => p.set_file_name(s.encoded_bytes()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with<K, T>(kind: K, strand: T) -> Result<Self, StrandError>
|
||||
pub fn with<K>(kind: K, bytes: Vec<u8>) -> Result<Self, PathDynError>
|
||||
where
|
||||
K: Into<PathKind>,
|
||||
T: AsStrand,
|
||||
{
|
||||
let s = strand.as_strand();
|
||||
Ok(match kind.into() {
|
||||
PathKind::Os => Self::Os(std::path::PathBuf::from(s.as_os()?)),
|
||||
PathKind::Os => {
|
||||
Self::Os(std::path::PathBuf::from_wtf8_vec(bytes).map_err(|_| PathDynError::AsOs)?)
|
||||
}
|
||||
PathKind::Unix => Self::Unix(bytes.into()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +139,19 @@ impl PathBufDyn {
|
|||
{
|
||||
match kind.into() {
|
||||
PathKind::Os => Self::Os(std::path::PathBuf::with_capacity(capacity)),
|
||||
PathKind::Unix => Self::Unix(typed_path::UnixPathBuf::with_capacity(capacity)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_str<K, S>(kind: K, s: S) -> Self
|
||||
where
|
||||
K: Into<PathKind>,
|
||||
S: Into<String>,
|
||||
{
|
||||
let s = s.into();
|
||||
match kind.into() {
|
||||
PathKind::Os => Self::Os(s.into()),
|
||||
PathKind::Unix => Self::Unix(s.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
use std::path::PrefixComponent;
|
||||
|
||||
use crate::{path::PathDynError, strand::Strand};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Component<'a> {
|
||||
Prefix(PrefixComponent<'a>),
|
||||
RootDir,
|
||||
CurDir,
|
||||
ParentDir,
|
||||
Normal(Strand<'a>),
|
||||
}
|
||||
|
||||
impl<'a> From<std::path::Component<'a>> for Component<'a> {
|
||||
fn from(value: std::path::Component<'a>) -> Self {
|
||||
match value {
|
||||
std::path::Component::Prefix(p) => Self::Prefix(p),
|
||||
std::path::Component::RootDir => Self::RootDir,
|
||||
std::path::Component::CurDir => Self::CurDir,
|
||||
std::path::Component::ParentDir => Self::ParentDir,
|
||||
std::path::Component::Normal(s) => Self::Normal(Strand::Os(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<typed_path::UnixComponent<'a>> for Component<'a> {
|
||||
fn from(value: typed_path::UnixComponent<'a>) -> Self {
|
||||
match value {
|
||||
typed_path::UnixComponent::RootDir => Self::RootDir,
|
||||
typed_path::UnixComponent::CurDir => Self::CurDir,
|
||||
typed_path::UnixComponent::ParentDir => Self::ParentDir,
|
||||
typed_path::UnixComponent::Normal(b) => Self::Normal(Strand::Bytes(b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Component<'a>> for std::path::Component<'a> {
|
||||
type Error = PathDynError;
|
||||
|
||||
fn try_from(value: Component<'a>) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
Component::Prefix(p) => Self::Prefix(p),
|
||||
Component::RootDir => Self::RootDir,
|
||||
Component::CurDir => Self::CurDir,
|
||||
Component::ParentDir => Self::ParentDir,
|
||||
Component::Normal(s) => Self::Normal(s.as_os()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Component<'a>> for typed_path::UnixComponent<'a> {
|
||||
type Error = PathDynError;
|
||||
|
||||
fn try_from(value: Component<'a>) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
Component::Prefix(_) => Err(PathDynError::AsUnix)?,
|
||||
Component::RootDir => Self::RootDir,
|
||||
Component::CurDir => Self::CurDir,
|
||||
Component::ParentDir => Self::ParentDir,
|
||||
Component::Normal(s) => Self::Normal(s.encoded_bytes()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Component<'a>> for Result<std::path::PathBuf, PathDynError> {
|
||||
fn from_iter<I: IntoIterator<Item = Component<'a>>>(iter: I) -> Self {
|
||||
iter.into_iter().map(std::path::Component::try_from).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Component<'a>> for Result<typed_path::UnixPathBuf, PathDynError> {
|
||||
fn from_iter<I: IntoIterator<Item = Component<'a>>>(iter: I) -> Self {
|
||||
iter.into_iter().map(typed_path::UnixComponent::try_from).collect()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
use std::iter::FusedIterator;
|
||||
|
||||
use typed_path::Components as _;
|
||||
|
||||
use crate::{path::{Component, PathDyn}, strand::Strand};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Components<'a> {
|
||||
Os(std::path::Components<'a>),
|
||||
Unix(typed_path::UnixComponents<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Components<'a> {
|
||||
pub fn path(&self) -> PathDyn<'a> {
|
||||
match self {
|
||||
Self::Os(c) => PathDyn::Os(c.as_path()),
|
||||
Self::Unix(c) => PathDyn::Unix(c.as_path()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strand(&self) -> Strand<'a> {
|
||||
match self {
|
||||
Self::Os(c) => Strand::Os(c.as_path().as_os_str()),
|
||||
Self::Unix(c) => Strand::Bytes(c.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Components<'a> {
|
||||
type Item = Component<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Component<'a>> {
|
||||
match self {
|
||||
Self::Os(c) => c.next().map(Into::into),
|
||||
Self::Unix(c) => c.next().map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for Components<'a> {
|
||||
fn next_back(&mut self) -> Option<Component<'a>> {
|
||||
match self {
|
||||
Self::Os(c) => c.next_back().map(Into::into),
|
||||
Self::Unix(c) => c.next_back().map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for Components<'_> {}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
use std::ffi::OsStr;
|
||||
|
||||
use super::{PathBufDyn, PathDyn};
|
||||
use crate::path::PathCow;
|
||||
|
||||
|
|
@ -8,14 +6,6 @@ pub trait AsPath {
|
|||
fn as_path(&self) -> PathDyn<'_>;
|
||||
}
|
||||
|
||||
impl AsPath for OsStr {
|
||||
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self.as_ref()) }
|
||||
}
|
||||
|
||||
impl AsPath for &OsStr {
|
||||
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self.as_ref()) }
|
||||
}
|
||||
|
||||
impl AsPath for std::path::Path {
|
||||
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self) }
|
||||
}
|
||||
|
|
@ -32,14 +22,11 @@ impl AsPath for PathBufDyn {
|
|||
fn as_path(&self) -> PathDyn<'_> {
|
||||
match self {
|
||||
Self::Os(p) => PathDyn::Os(p),
|
||||
Self::Unix(p) => PathDyn::Unix(p),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPath for &PathBufDyn {
|
||||
fn as_path(&self) -> PathDyn<'_> { (*self).as_path() }
|
||||
}
|
||||
|
||||
impl AsPath for PathCow<'_> {
|
||||
fn as_path(&self) -> PathDyn<'_> {
|
||||
match self {
|
||||
|
|
@ -49,6 +36,10 @@ impl AsPath for PathCow<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsPath for super::Components<'_> {
|
||||
fn as_path(&self) -> PathDyn<'_> { self.path() }
|
||||
}
|
||||
|
||||
// --- AsPathRef
|
||||
pub trait AsPathRef<'a> {
|
||||
fn as_path_ref(self) -> PathDyn<'a>;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::borrow::Cow;
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{IntoOsStr, path::{AsPath, PathBufDyn, PathDyn}};
|
||||
use crate::path::{AsPath, PathBufDyn, PathDyn, PathKind};
|
||||
|
||||
// --- PathCow
|
||||
pub enum PathCow<'a> {
|
||||
|
|
@ -32,17 +32,21 @@ impl PartialEq<&str> for PathCow<'_> {
|
|||
}
|
||||
|
||||
impl<'a> PathCow<'a> {
|
||||
pub fn from_os_bytes(bytes: impl Into<Cow<'a, [u8]>>) -> Result<Self> {
|
||||
Ok(match bytes.into().into_os_str()? {
|
||||
Cow::Borrowed(s) => PathDyn::os(s).into(),
|
||||
Cow::Owned(s) => PathBufDyn::os(s).into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_owned(self) -> PathBufDyn {
|
||||
match self {
|
||||
Self::Borrowed(s) => s.to_owned(),
|
||||
Self::Owned(s) => s,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with<K, T>(kind: K, bytes: T) -> Result<Self>
|
||||
where
|
||||
K: Into<PathKind>,
|
||||
T: Into<Cow<'a, [u8]>>,
|
||||
{
|
||||
Ok(match bytes.into() {
|
||||
Cow::Borrowed(b) => PathDyn::with(kind, b)?.into(),
|
||||
Cow::Owned(b) => PathBufDyn::with(kind, b)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
yazi-shared/src/path/display.rs
Normal file
12
yazi-shared/src/path/display.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
use crate::path::PathDyn;
|
||||
|
||||
pub struct Display<'a>(pub PathDyn<'a>);
|
||||
|
||||
impl std::fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.0 {
|
||||
PathDyn::Os(p) => write!(f, "{}", p.display()),
|
||||
PathDyn::Unix(p) => write!(f, "{}", p.display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ pub struct EndsWithError;
|
|||
#[error("calling join on paths with different encodings")]
|
||||
pub enum JoinError {
|
||||
FromWtf8,
|
||||
FromPathBufDyn(#[from] PathBufDynError),
|
||||
FromPathDyn(#[from] PathDynError),
|
||||
}
|
||||
|
||||
impl From<JoinError> for std::io::Error {
|
||||
|
|
@ -22,27 +22,33 @@ impl From<JoinError> for std::io::Error {
|
|||
// --- PathDynError
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PathDynError {
|
||||
#[error("conversion to OsStr failed")]
|
||||
#[error("conversion to OS path failed")]
|
||||
AsOs,
|
||||
#[error("conversion to Unix path failed")]
|
||||
AsUnix,
|
||||
#[error("conversion to UTF-8 path failed")]
|
||||
AsUtf8,
|
||||
}
|
||||
|
||||
impl From<StrandError> for PathDynError {
|
||||
fn from(err: StrandError) -> Self {
|
||||
match err {
|
||||
StrandError::AsOs => Self::AsOs,
|
||||
StrandError::AsUtf8 => Self::AsUtf8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathDynError> for std::io::Error {
|
||||
fn from(err: PathDynError) -> Self { Self::other(err) }
|
||||
}
|
||||
|
||||
// --- PathBufDynError
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PathBufDynError {
|
||||
#[error("conversion to OsString failed")]
|
||||
IntoOs,
|
||||
}
|
||||
|
||||
// --- SetNameError
|
||||
#[derive(Debug, Error)]
|
||||
#[error("calling set_name on paths with different encodings")]
|
||||
pub enum SetNameError {
|
||||
FromWtf8,
|
||||
FromStrandDyn(#[from] StrandError),
|
||||
FromStrand(#[from] StrandError),
|
||||
}
|
||||
|
||||
impl From<SetNameError> for std::io::Error {
|
||||
|
|
@ -89,3 +95,7 @@ pub enum StripPrefixError {
|
|||
impl From<std::path::StripPrefixError> for StripPrefixError {
|
||||
fn from(_: std::path::StripPrefixError) -> Self { Self::NotPrefix }
|
||||
}
|
||||
|
||||
impl From<typed_path::StripPrefixError> for StripPrefixError {
|
||||
fn from(_: typed_path::StripPrefixError) -> Self { Self::NotPrefix }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::scheme::SchemeKind;
|
|||
|
||||
pub enum PathKind {
|
||||
Os,
|
||||
Unix,
|
||||
}
|
||||
|
||||
impl From<SchemeKind> for PathKind {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use std::borrow::Cow;
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{Utf8BytePredictor, path::{AsPath, EndsWithError, JoinError, PathBufDyn, PathCow, PathDyn, PathDynError, PathKind, RsplitOnceError, StartsWithError, StripPrefixError}, strand::{AsStrand, Strand}};
|
||||
use crate::{Utf8BytePredictor, path::{AsPath, Components, Display, EndsWithError, JoinError, PathBufDyn, PathCow, PathDyn, PathDynError, PathKind, RsplitOnceError, StartsWithError, StripPrefixError}, strand::{AsStrand, Strand}};
|
||||
|
||||
pub trait PathLike: AsPath {
|
||||
fn as_os(&self) -> Result<&std::path::Path, PathDynError> { self.as_path().as_os() }
|
||||
|
||||
fn components(&self) -> std::path::Components<'_> { self.as_path().components() }
|
||||
fn components(&self) -> Components<'_> { self.as_path().components() }
|
||||
|
||||
fn display(&self) -> std::path::Display<'_> { self.as_path().display() }
|
||||
fn display(&self) -> Display<'_> { self.as_path().display() }
|
||||
|
||||
fn encoded_bytes(&self) -> &[u8] { self.as_path().encoded_bytes() }
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(buf component components conversion cow error kind like path view);
|
||||
yazi_macro::mod_flat!(buf component components conversion cow display error kind like path view);
|
||||
|
|
|
|||
|
|
@ -4,17 +4,22 @@ use anyhow::Result;
|
|||
use hashbrown::Equivalent;
|
||||
|
||||
use super::{RsplitOnceError, StartsWithError};
|
||||
use crate::{BytesExt, FromWtf8, Utf8BytePredictor, path::{AsPath, EndsWithError, JoinError, PathBufDyn, PathDynError, PathKind, StripPrefixError}, strand::{AsStrand, Strand, StrandError}};
|
||||
use crate::{BytesExt, FromWtf8, Utf8BytePredictor, path::{AsPath, Components, Display, EndsWithError, JoinError, PathBufDyn, PathDynError, PathKind, StripPrefixError}, strand::{AsStrand, Strand, StrandError}};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum PathDyn<'p> {
|
||||
Os(&'p std::path::Path),
|
||||
Unix(&'p typed_path::UnixPath),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a std::path::Path> for PathDyn<'a> {
|
||||
fn from(value: &'a std::path::Path) -> Self { Self::Os(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a typed_path::UnixPath> for PathDyn<'a> {
|
||||
fn from(value: &'a typed_path::UnixPath) -> Self { Self::Unix(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a PathBufDyn> for PathDyn<'a> {
|
||||
fn from(value: &'a PathBufDyn) -> Self { value.as_path() }
|
||||
}
|
||||
|
|
@ -33,8 +38,9 @@ impl PartialEq<&std::path::Path> for PathDyn<'_> {
|
|||
|
||||
impl PartialEq<&str> for PathDyn<'_> {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
match self {
|
||||
PathDyn::Os(p) => p == other,
|
||||
match *self {
|
||||
PathDyn::Os(p) => p == *other,
|
||||
PathDyn::Unix(p) => p == typed_path::UnixPath::new(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,30 +54,30 @@ impl<'p> PathDyn<'p> {
|
|||
pub fn as_os(self) -> Result<&'p std::path::Path, PathDynError> {
|
||||
match self {
|
||||
Self::Os(p) => Ok(p),
|
||||
Self::Unix(_) => Err(PathDynError::AsOs),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn components(self) -> std::path::Components<'p> {
|
||||
pub fn components(self) -> Components<'p> {
|
||||
match self {
|
||||
Self::Os(p) => p.components(),
|
||||
Self::Os(p) => Components::Os(p.components()),
|
||||
Self::Unix(p) => Components::Unix(p.components()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(self) -> std::path::Display<'p> {
|
||||
match self {
|
||||
Self::Os(p) => p.display(),
|
||||
}
|
||||
}
|
||||
pub fn display(self) -> Display<'p> { Display(self) }
|
||||
|
||||
pub fn encoded_bytes(self) -> &'p [u8] {
|
||||
match self {
|
||||
Self::Os(p) => p.as_os_str().as_encoded_bytes(),
|
||||
Self::Unix(p) => p.as_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ext(self) -> Option<Strand<'p>> {
|
||||
Some(match self {
|
||||
Self::Os(p) => p.extension()?.into(),
|
||||
Self::Unix(p) => p.extension()?.into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -82,18 +88,21 @@ impl<'p> PathDyn<'p> {
|
|||
{
|
||||
match kind.into() {
|
||||
PathKind::Os => Self::Os(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }.as_ref()),
|
||||
PathKind::Unix => Self::Unix(typed_path::UnixPath::new(bytes)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_root(self) -> bool {
|
||||
match self {
|
||||
Self::Os(p) => p.has_root(),
|
||||
Self::Unix(p) => p.has_root(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_absolute(self) -> bool {
|
||||
match self {
|
||||
Self::Os(p) => p.is_absolute(),
|
||||
Self::Unix(p) => p.is_absolute(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +116,7 @@ impl<'p> PathDyn<'p> {
|
|||
pub fn kind(self) -> PathKind {
|
||||
match self {
|
||||
Self::Os(_) => PathKind::Os,
|
||||
Self::Unix(_) => PathKind::Unix,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,20 +125,14 @@ impl<'p> PathDyn<'p> {
|
|||
pub fn name(self) -> Option<Strand<'p>> {
|
||||
Some(match self {
|
||||
Self::Os(p) => p.file_name()?.into(),
|
||||
Self::Unix(p) => p.file_name()?.into(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn os<T>(path: &'p T) -> Self
|
||||
where
|
||||
T: ?Sized + AsRef<std::path::Path>,
|
||||
{
|
||||
Self::Os(path.as_ref())
|
||||
}
|
||||
|
||||
pub fn parent(self) -> Option<Self> {
|
||||
Some(match self {
|
||||
Self::Os(p) => Self::Os(p.parent()?),
|
||||
Self::Unix(p) => Self::Unix(p.parent()?),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +149,7 @@ impl<'p> PathDyn<'p> {
|
|||
pub fn stem(self) -> Option<Strand<'p>> {
|
||||
Some(match self {
|
||||
Self::Os(p) => p.file_stem()?.into(),
|
||||
Self::Unix(p) => p.file_stem()?.into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -152,12 +157,14 @@ impl<'p> PathDyn<'p> {
|
|||
pub fn to_os_owned(self) -> Result<std::path::PathBuf, PathDynError> {
|
||||
match self {
|
||||
Self::Os(p) => Ok(p.to_owned()),
|
||||
Self::Unix(_) => Err(PathDynError::AsOs),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_owned(self) -> PathBufDyn {
|
||||
match self {
|
||||
Self::Os(p) => PathBufDyn::Os(p.to_path_buf()),
|
||||
Self::Os(p) => PathBufDyn::Os(p.to_owned()),
|
||||
Self::Unix(p) => PathBufDyn::Unix(p.to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +184,8 @@ impl<'p> PathDyn<'p> {
|
|||
(Self::Os(p), Strand::Bytes(b)) => {
|
||||
p.ends_with(OsStr::from_wtf8(b).map_err(|_| EndsWithError)?)
|
||||
}
|
||||
|
||||
(Self::Unix(p), s) => p.ends_with(s.encoded_bytes()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +199,8 @@ impl<'p> PathDyn<'p> {
|
|||
(Self::Os(p), Strand::Bytes(b)) => {
|
||||
PathBufDyn::Os(p.join(OsStr::from_wtf8(b).map_err(|_| JoinError::FromWtf8)?))
|
||||
}
|
||||
|
||||
(Self::Unix(p), s) => PathBufDyn::Unix(p.join(s.encoded_bytes())),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -203,6 +214,7 @@ impl<'p> PathDyn<'p> {
|
|||
PathDyn::Os(p) => {
|
||||
p.as_os_str().as_encoded_bytes().rsplit_seq_once(pat.as_os()?.as_encoded_bytes())
|
||||
}
|
||||
PathDyn::Unix(p) => p.as_bytes().rsplit_seq_once(pat.encoded_bytes()),
|
||||
}
|
||||
.ok_or(RsplitOnceError::NotFound)?;
|
||||
|
||||
|
|
@ -216,11 +228,13 @@ impl<'p> PathDyn<'p> {
|
|||
T: AsStrand,
|
||||
{
|
||||
Ok(match (self, base.as_strand()) {
|
||||
(Self::Os(p), Strand::Os(q)) => p.starts_with(q),
|
||||
(Self::Os(p), Strand::Utf8(q)) => p.starts_with(q),
|
||||
(Self::Os(p), Strand::Os(s)) => p.starts_with(s),
|
||||
(Self::Os(p), Strand::Utf8(s)) => p.starts_with(s),
|
||||
(Self::Os(p), Strand::Bytes(b)) => {
|
||||
p.starts_with(OsStr::from_wtf8(b).map_err(|_| StartsWithError)?)
|
||||
}
|
||||
|
||||
(Self::Unix(p), s) => p.starts_with(s.encoded_bytes()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -229,22 +243,37 @@ impl<'p> PathDyn<'p> {
|
|||
T: AsStrand,
|
||||
{
|
||||
Ok(match (self, base.as_strand()) {
|
||||
(Self::Os(p), Strand::Os(q)) => Self::Os(p.strip_prefix(q)?),
|
||||
(Self::Os(p), Strand::Utf8(q)) => Self::Os(p.strip_prefix(q)?),
|
||||
(Self::Os(p), Strand::Os(s)) => Self::Os(p.strip_prefix(s)?),
|
||||
(Self::Os(p), Strand::Utf8(s)) => Self::Os(p.strip_prefix(s)?),
|
||||
(Self::Os(p), Strand::Bytes(b)) => {
|
||||
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())?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with<K, T>(kind: K, strand: &'p T) -> Result<Self, StrandError>
|
||||
pub fn with<K, S>(kind: K, strand: &'p S) -> Result<Self, StrandError>
|
||||
where
|
||||
K: Into<PathKind>,
|
||||
T: ?Sized + AsStrand,
|
||||
S: ?Sized + AsStrand,
|
||||
{
|
||||
let s = strand.as_strand();
|
||||
Ok(match kind.into() {
|
||||
PathKind::Os => Self::Os(std::path::Path::new(s.as_os()?)),
|
||||
PathKind::Unix => Self::Unix(typed_path::UnixPath::new(s.encoded_bytes())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_str<K, S>(kind: K, s: &'p S) -> Self
|
||||
where
|
||||
K: Into<PathKind>,
|
||||
S: ?Sized + AsRef<str>,
|
||||
{
|
||||
let s = s.as_ref();
|
||||
match kind.into() {
|
||||
PathKind::Os => Self::Os(s.as_ref()),
|
||||
PathKind::Unix => Self::Unix(s.as_ref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
use std::{borrow::Cow, ffi::OsStr};
|
||||
|
||||
use crate::path::{AsPath, PathDyn};
|
||||
|
||||
// --- AsPathView
|
||||
pub trait AsPathView<'a, T> {
|
||||
fn as_path_view(self) -> T;
|
||||
}
|
||||
|
||||
impl<'a> AsPathView<'a, &'a std::path::Path> for &'a OsStr {
|
||||
fn as_path_view(self) -> &'a std::path::Path { std::path::Path::new(self) }
|
||||
}
|
||||
|
||||
impl<'a> AsPathView<'a, &'a std::path::Path> for &'a std::path::Path {
|
||||
fn as_path_view(self) -> &'a std::path::Path { self }
|
||||
}
|
||||
|
|
@ -18,22 +10,3 @@ impl<'a> AsPathView<'a, &'a std::path::Path> for &'a std::path::Path {
|
|||
impl<'a> AsPathView<'a, &'a std::path::Path> for &'a std::path::PathBuf {
|
||||
fn as_path_view(self) -> &'a std::path::Path { self }
|
||||
}
|
||||
|
||||
impl<'a> AsPathView<'a, &'a std::path::Path> for std::path::Components<'a> {
|
||||
fn as_path_view(self) -> &'a std::path::Path { self.as_path() }
|
||||
}
|
||||
|
||||
impl<'a, T> AsPathView<'a, PathDyn<'a>> for &'a T
|
||||
where
|
||||
T: AsPath,
|
||||
{
|
||||
fn as_path_view(self) -> PathDyn<'a> { self.as_path() }
|
||||
}
|
||||
|
||||
impl<'a> AsPathView<'a, &'a std::path::Path> for &'a Cow<'_, OsStr> {
|
||||
fn as_path_view(self) -> &'a std::path::Path { std::path::Path::new(self) }
|
||||
}
|
||||
|
||||
impl<'a> AsPathView<'a, &'a std::path::Path> for crate::loc::Loc<'a, &'a std::path::Path> {
|
||||
fn as_path_view(self) -> &'a std::path::Path { *self }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub struct LcgRng {
|
||||
seed: u64,
|
||||
}
|
||||
|
||||
impl LcgRng {
|
||||
const A: u64 = 6364136223846793005;
|
||||
const C: u64 = 1;
|
||||
const M: u64 = u64::MAX;
|
||||
}
|
||||
|
||||
impl Iterator for LcgRng {
|
||||
type Item = u64;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.seed = Self::A.wrapping_mul(self.seed).wrapping_add(Self::C) % Self::M;
|
||||
Some(self.seed)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LcgRng {
|
||||
fn default() -> Self {
|
||||
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
|
||||
Self { seed: time.as_secs() ^ time.subsec_nanos() as u64 }
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,10 @@ impl From<SchemeCow<'_>> for Scheme {
|
|||
fn from(value: SchemeCow<'_>) -> Self { value.into_owned() }
|
||||
}
|
||||
|
||||
impl PartialEq<SchemeRef<'_>> for SchemeCow<'_> {
|
||||
fn eq(&self, other: &SchemeRef) -> bool { self.as_scheme() == *other }
|
||||
}
|
||||
|
||||
impl<'a> SchemeCow<'a> {
|
||||
pub fn regular(uri: usize, urn: usize) -> Self { SchemeRef::Regular { uri, urn }.into() }
|
||||
|
||||
|
|
@ -130,12 +134,7 @@ impl<'a> SchemeCow<'a> {
|
|||
|
||||
fn decode_path(kind: SchemeKind, tilde: bool, bytes: &'a [u8]) -> Result<PathCow<'a>> {
|
||||
let bytes: Cow<_> = if tilde { percent_decode(bytes).into() } else { bytes.into() };
|
||||
Ok(match kind {
|
||||
SchemeKind::Regular => PathCow::from_os_bytes(bytes)?,
|
||||
SchemeKind::Search => PathCow::from_os_bytes(bytes)?,
|
||||
SchemeKind::Archive => PathCow::from_os_bytes(bytes)?,
|
||||
SchemeKind::Sftp => PathCow::from_os_bytes(bytes)?,
|
||||
})
|
||||
PathCow::with(kind, bytes)
|
||||
}
|
||||
|
||||
fn normalize_ports(
|
||||
|
|
|
|||
|
|
@ -28,4 +28,14 @@ impl Scheme {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_ports(self, uri: usize, urn: usize) -> Self {
|
||||
match self {
|
||||
Self::Regular { .. } => Self::Regular { uri, urn },
|
||||
Self::Search { domain, .. } => Self::Search { domain, uri, urn },
|
||||
Self::Archive { domain, .. } => Self::Archive { domain, uri, urn },
|
||||
Self::Sftp { domain, .. } => Self::Sftp { domain, uri, urn },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use std::ffi::OsString;
|
||||
use std::{borrow::Cow, ffi::OsString};
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{path::PathDyn, scheme::SchemeKind, strand::{AsStrand, Strand, StrandError, StrandKind}};
|
||||
use crate::{FromWtf8Vec, path::PathDyn, strand::{AsStrand, Strand, StrandCow, StrandError, StrandKind}};
|
||||
|
||||
// --- StrandBuf
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
|
|
@ -21,6 +22,10 @@ impl From<OsString> for StrandBuf {
|
|||
fn from(value: OsString) -> Self { Self::Os(value) }
|
||||
}
|
||||
|
||||
impl From<&str> for StrandBuf {
|
||||
fn from(value: &str) -> Self { Self::Utf8(value.to_owned()) }
|
||||
}
|
||||
|
||||
impl From<String> for StrandBuf {
|
||||
fn from(value: String) -> Self { Self::Utf8(value) }
|
||||
}
|
||||
|
|
@ -29,10 +34,15 @@ impl From<PathDyn<'_>> for StrandBuf {
|
|||
fn from(value: PathDyn) -> Self {
|
||||
match value {
|
||||
PathDyn::Os(p) => Self::Os(p.as_os_str().to_owned()),
|
||||
PathDyn::Unix(p) => Self::Bytes(p.as_bytes().to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StrandCow<'_>> for StrandBuf {
|
||||
fn from(value: StrandCow<'_>) -> Self { value.into_owned() }
|
||||
}
|
||||
|
||||
impl PartialEq<Strand<'_>> for StrandBuf {
|
||||
fn eq(&self, other: &Strand<'_>) -> bool { self.as_strand() == *other }
|
||||
}
|
||||
|
|
@ -49,8 +59,8 @@ impl StrandBuf {
|
|||
#[inline]
|
||||
pub unsafe fn from_encoded_bytes(kind: impl Into<StrandKind>, bytes: Vec<u8>) -> Self {
|
||||
match kind.into() {
|
||||
StrandKind::Os => Self::Os(unsafe { OsString::from_encoded_bytes_unchecked(bytes) }),
|
||||
StrandKind::Utf8 => Self::Utf8(unsafe { String::from_utf8_unchecked(bytes) }),
|
||||
StrandKind::Os => Self::Os(unsafe { OsString::from_encoded_bytes_unchecked(bytes) }),
|
||||
StrandKind::Bytes => Self::Bytes(bytes),
|
||||
}
|
||||
}
|
||||
|
|
@ -63,6 +73,39 @@ impl StrandBuf {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn into_string_lossy(self) -> String {
|
||||
match self {
|
||||
Self::Os(s) => match s.to_string_lossy() {
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(s.into_encoded_bytes()) },
|
||||
Cow::Owned(s) => s,
|
||||
},
|
||||
Self::Utf8(s) => s,
|
||||
Self::Bytes(b) => match String::from_utf8_lossy(&b) {
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(b) },
|
||||
Cow::Owned(s) => s,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(kind: impl Into<StrandKind>) -> Self { Self::with_str(kind, "") }
|
||||
|
||||
pub fn push_str(&mut self, s: impl AsRef<str>) {
|
||||
let s = s.as_ref();
|
||||
match self {
|
||||
Self::Os(buf) => buf.push(s),
|
||||
Self::Utf8(buf) => buf.push_str(s),
|
||||
Self::Bytes(buf) => buf.extend(s.as_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_exact(&mut self, additional: usize) {
|
||||
match self {
|
||||
Self::Os(buf) => buf.reserve_exact(additional),
|
||||
Self::Utf8(buf) => buf.reserve_exact(additional),
|
||||
Self::Bytes(buf) => buf.reserve_exact(additional),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_push<T>(&mut self, s: T) -> Result<(), StrandError>
|
||||
where
|
||||
T: AsStrand,
|
||||
|
|
@ -75,11 +118,38 @@ impl StrandBuf {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn with_capacity(kind: SchemeKind, capacity: usize) -> Self {
|
||||
use SchemeKind as K;
|
||||
match kind {
|
||||
K::Regular | K::Search | K::Archive => Self::Os(OsString::with_capacity(capacity)),
|
||||
K::Sftp => Self::Os(OsString::with_capacity(capacity)), // FIXME
|
||||
pub fn with<K>(kind: K, bytes: Vec<u8>) -> Result<Self>
|
||||
where
|
||||
K: Into<StrandKind>,
|
||||
{
|
||||
Ok(match kind.into() {
|
||||
StrandKind::Utf8 => Self::Utf8(String::from_utf8(bytes)?),
|
||||
StrandKind::Os => Self::Os(OsString::from_wtf8_vec(bytes)?),
|
||||
StrandKind::Bytes => Self::Bytes(bytes),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_capacity<K>(kind: K, capacity: usize) -> Self
|
||||
where
|
||||
K: Into<StrandKind>,
|
||||
{
|
||||
match kind.into() {
|
||||
StrandKind::Utf8 => Self::Utf8(String::with_capacity(capacity)),
|
||||
StrandKind::Os => Self::Os(OsString::with_capacity(capacity)),
|
||||
StrandKind::Bytes => Self::Bytes(Vec::with_capacity(capacity)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_str<K, S>(kind: K, s: S) -> Self
|
||||
where
|
||||
K: Into<StrandKind>,
|
||||
S: Into<String>,
|
||||
{
|
||||
let s = s.into();
|
||||
match kind.into() {
|
||||
StrandKind::Utf8 => Self::Utf8(s),
|
||||
StrandKind::Os => Self::Os(s.into()),
|
||||
StrandKind::Bytes => Self::Bytes(s.into_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}};
|
||||
|
||||
use crate::{path::{PathBufDyn, PathDyn}, strand::{Strand, StrandBuf, StrandCow}};
|
||||
use crate::{path::{PathBufDyn, PathCow, PathDyn}, strand::{Strand, StrandBuf, StrandCow}, url::AsUrl};
|
||||
|
||||
// --- AsStrand
|
||||
pub trait AsStrand {
|
||||
|
|
@ -11,14 +11,6 @@ impl AsStrand for [u8] {
|
|||
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
|
||||
}
|
||||
|
||||
impl AsStrand for &[u8] {
|
||||
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
|
||||
}
|
||||
|
||||
impl AsStrand for Vec<u8> {
|
||||
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
|
||||
}
|
||||
|
||||
impl AsStrand for str {
|
||||
fn as_strand(&self) -> Strand<'_> { Strand::Utf8(self) }
|
||||
}
|
||||
|
|
@ -51,6 +43,10 @@ impl AsStrand for &std::path::Path {
|
|||
fn as_strand(&self) -> Strand<'_> { Strand::Os(self.as_os_str()) }
|
||||
}
|
||||
|
||||
impl AsStrand for crate::path::Components<'_> {
|
||||
fn as_strand(&self) -> Strand<'_> { self.strand() }
|
||||
}
|
||||
|
||||
impl AsStrand for Cow<'_, OsStr> {
|
||||
fn as_strand(&self) -> Strand<'_> { Strand::Os(self) }
|
||||
}
|
||||
|
|
@ -59,14 +55,29 @@ impl AsStrand for PathDyn<'_> {
|
|||
fn as_strand(&self) -> Strand<'_> {
|
||||
match self {
|
||||
Self::Os(p) => Strand::Os(p.as_os_str()),
|
||||
Self::Unix(p) => Strand::Bytes(p.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsStrand for PathBufDyn {
|
||||
fn as_strand(&self) -> Strand<'_> {
|
||||
match self {
|
||||
PathBufDyn::Os(p) => Strand::Os(p.as_os_str()),
|
||||
PathBufDyn::Unix(p) => Strand::Bytes(p.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsStrand for &PathBufDyn {
|
||||
fn as_strand(&self) -> Strand<'_> { (**self).as_strand() }
|
||||
}
|
||||
|
||||
impl AsStrand for PathCow<'_> {
|
||||
fn as_strand(&self) -> Strand<'_> {
|
||||
match self {
|
||||
PathBufDyn::Os(p) => Strand::Os(p.as_os_str()),
|
||||
Self::Borrowed(p) => p.as_strand(),
|
||||
Self::Owned(p) => p.as_strand(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,3 +112,19 @@ impl AsStrand for StrandCow<'_> {
|
|||
impl AsStrand for &StrandCow<'_> {
|
||||
fn as_strand(&self) -> Strand<'_> { (**self).as_strand() }
|
||||
}
|
||||
|
||||
// --- ToStrand
|
||||
pub trait ToStrand {
|
||||
fn to_strand(&self) -> StrandCow<'_>;
|
||||
}
|
||||
|
||||
impl ToStrand for String {
|
||||
fn to_strand(&self) -> StrandCow<'_> { self.as_strand().into() }
|
||||
}
|
||||
|
||||
impl<T> ToStrand for T
|
||||
where
|
||||
T: AsUrl,
|
||||
{
|
||||
fn to_strand(&self) -> StrandCow<'_> { self.as_url().components().strand() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@ use std::{borrow::Cow, ffi::{OsStr, OsString}};
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{IntoOsStr, scheme::SchemeKind, strand::{Strand, StrandBuf}};
|
||||
use crate::strand::{Strand, StrandBuf, StrandKind};
|
||||
|
||||
// --- StrandCow
|
||||
pub enum StrandCow<'a> {
|
||||
Borrowed(Strand<'a>),
|
||||
Owned(StrandBuf),
|
||||
}
|
||||
|
||||
impl Default for StrandCow<'_> {
|
||||
fn default() -> Self { Self::Borrowed(Strand::default()) }
|
||||
}
|
||||
|
||||
impl From<OsString> for StrandCow<'_> {
|
||||
fn from(value: OsString) -> Self { Self::Owned(StrandBuf::Os(value)) }
|
||||
}
|
||||
|
|
@ -41,13 +44,6 @@ impl PartialEq<Strand<'_>> for StrandCow<'_> {
|
|||
}
|
||||
|
||||
impl<'a> StrandCow<'a> {
|
||||
pub fn from_os_bytes(bytes: impl Into<Cow<'a, [u8]>>) -> Result<Self> {
|
||||
Ok(match bytes.into().into_os_str()? {
|
||||
Cow::Borrowed(s) => Strand::Os(s).into(),
|
||||
Cow::Owned(s) => StrandBuf::Os(s).into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_owned(self) -> StrandBuf {
|
||||
match self {
|
||||
Self::Borrowed(s) => s.to_owned(),
|
||||
|
|
@ -55,13 +51,21 @@ impl<'a> StrandCow<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with<T>(kind: SchemeKind, bytes: T) -> Result<Self>
|
||||
where
|
||||
T: Into<Cow<'a, [u8]>>,
|
||||
{
|
||||
match kind {
|
||||
SchemeKind::Regular | SchemeKind::Search | SchemeKind::Archive => Self::from_os_bytes(bytes),
|
||||
SchemeKind::Sftp => Self::from_os_bytes(bytes), // FIXME
|
||||
pub fn into_string_lossy(self) -> String {
|
||||
match self {
|
||||
Self::Borrowed(s) => s.to_string_lossy().into_owned(),
|
||||
Self::Owned(s) => s.into_string_lossy(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with<K, T>(kind: K, bytes: T) -> Result<Self>
|
||||
where
|
||||
K: Into<StrandKind>,
|
||||
T: Into<Cow<'a, [u8]>>,
|
||||
{
|
||||
Ok(match bytes.into() {
|
||||
Cow::Borrowed(b) => Strand::with(kind, b)?.into(),
|
||||
Cow::Owned(b) => StrandBuf::with(kind, b)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,14 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::path::PathDynError;
|
||||
|
||||
// --- StrandDynError
|
||||
// --- StrandError
|
||||
#[derive(Debug, Error)]
|
||||
pub enum StrandError {
|
||||
#[error("conversion to OsStr failed")]
|
||||
#[error("conversion to OS string failed")]
|
||||
AsOs,
|
||||
#[error("conversion to UTF-8 str failed")]
|
||||
#[error("conversion to UTF-8 string failed")]
|
||||
AsUtf8,
|
||||
}
|
||||
|
||||
impl From<PathDynError> for StrandError {
|
||||
fn from(err: PathDynError) -> Self {
|
||||
match err {
|
||||
PathDynError::AsOs => Self::AsOs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StrandError> for std::io::Error {
|
||||
fn from(err: StrandError) -> Self { Self::other(err) }
|
||||
}
|
||||
|
|
|
|||
61
yazi-shared/src/strand/extensions.rs
Normal file
61
yazi-shared/src/strand/extensions.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use crate::strand::{AsStrand, Strand, StrandBuf, StrandLike, ToStrand};
|
||||
|
||||
// --- StrandJoin
|
||||
pub trait AsStrandJoin {
|
||||
fn join(self, sep: Strand) -> StrandBuf;
|
||||
}
|
||||
|
||||
impl<T> AsStrandJoin for T
|
||||
where
|
||||
T: IntoIterator,
|
||||
T::Item: AsStrand,
|
||||
{
|
||||
fn join(self, sep: Strand) -> StrandBuf {
|
||||
let mut kind = sep.kind();
|
||||
let mut buf = Vec::new();
|
||||
for (i, item) in self.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
buf.extend(sep.encoded_bytes());
|
||||
}
|
||||
|
||||
let s = item.as_strand();
|
||||
buf.extend(s.encoded_bytes());
|
||||
|
||||
if s.kind() > kind {
|
||||
kind = s.kind();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { StrandBuf::from_encoded_bytes(kind, buf) }
|
||||
}
|
||||
}
|
||||
|
||||
// --- ToStrandJoin
|
||||
pub trait ToStrandJoin {
|
||||
fn join(self, sep: Strand) -> StrandBuf;
|
||||
}
|
||||
|
||||
impl<T> ToStrandJoin for T
|
||||
where
|
||||
T: IntoIterator,
|
||||
T::Item: ToStrand,
|
||||
{
|
||||
fn join(self, sep: Strand) -> StrandBuf {
|
||||
let mut kind = sep.kind();
|
||||
let mut buf = Vec::new();
|
||||
for (i, item) in self.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
buf.extend(sep.encoded_bytes());
|
||||
}
|
||||
|
||||
let s = item.to_strand();
|
||||
buf.extend(s.encoded_bytes());
|
||||
|
||||
if s.kind() > kind {
|
||||
kind = s.kind();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { StrandBuf::from_encoded_bytes(kind, buf) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,28 @@
|
|||
use crate::{path::PathKind, scheme::SchemeKind};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum StrandKind {
|
||||
Os,
|
||||
Utf8,
|
||||
Bytes,
|
||||
Utf8 = 0,
|
||||
Os = 1,
|
||||
Bytes = 2,
|
||||
}
|
||||
|
||||
impl From<PathKind> for StrandKind {
|
||||
fn from(value: PathKind) -> Self {
|
||||
match value {
|
||||
PathKind::Os => StrandKind::Os,
|
||||
PathKind::Unix => StrandKind::Bytes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SchemeKind> for StrandKind {
|
||||
fn from(value: SchemeKind) -> Self {
|
||||
match value {
|
||||
SchemeKind::Regular => Self::Os,
|
||||
SchemeKind::Search => Self::Os,
|
||||
SchemeKind::Archive => Self::Os,
|
||||
SchemeKind::Sftp => Self::Os, // FIXME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(buf conversion cow error kind like r#dyn view);
|
||||
yazi_macro::mod_flat!(buf conversion cow error extensions kind like strand view);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, fmt::Display};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{BytesExt, FromWtf8, strand::{AsStrand, StrandBuf, StrandError, StrandKind}};
|
||||
|
||||
// --- Strand
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd)]
|
||||
pub enum Strand<'p> {
|
||||
Os(&'p OsStr),
|
||||
Utf8(&'p str),
|
||||
|
|
@ -22,6 +24,10 @@ impl<'a> From<&'a str> for Strand<'a> {
|
|||
fn from(value: &'a str) -> Self { Self::Utf8(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for Strand<'a> {
|
||||
fn from(value: &'a [u8]) -> Self { Self::Bytes(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a StrandBuf> for Strand<'a> {
|
||||
fn from(value: &'a StrandBuf) -> Self {
|
||||
match value {
|
||||
|
|
@ -32,12 +38,42 @@ impl<'a> From<&'a StrandBuf> for Strand<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Strand<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match *other {
|
||||
Self::Os(s) => *self == s,
|
||||
Self::Utf8(s) => *self == s,
|
||||
Self::Bytes(b) => *self == b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&OsStr> for Strand<'_> {
|
||||
fn eq(&self, other: &&OsStr) -> bool {
|
||||
match *self {
|
||||
Self::Os(s) => s == *other,
|
||||
Self::Utf8(s) => s == *other,
|
||||
Self::Bytes(b) => b == other.as_encoded_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for Strand<'_> {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
match self {
|
||||
Self::Os(s) => s == other,
|
||||
Self::Utf8(s) => s == other,
|
||||
Self::Bytes(b) => *b == other.as_bytes(),
|
||||
match *self {
|
||||
Self::Os(s) => s == *other,
|
||||
Self::Utf8(s) => s == *other,
|
||||
Self::Bytes(b) => b == other.as_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&[u8]> for Strand<'_> {
|
||||
fn eq(&self, other: &&[u8]) -> bool {
|
||||
match *self {
|
||||
Self::Os(s) => s.as_encoded_bytes() == *other,
|
||||
Self::Utf8(s) => s.as_bytes() == *other,
|
||||
Self::Bytes(b) => b == *other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,6 +138,7 @@ impl<'a> Strand<'a> {
|
|||
D(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn encoded_bytes(self) -> &'a [u8] {
|
||||
match self {
|
||||
Self::Os(s) => s.as_encoded_bytes(),
|
||||
|
|
@ -117,8 +154,8 @@ impl<'a> Strand<'a> {
|
|||
#[inline]
|
||||
pub unsafe fn from_encoded_bytes(kind: impl Into<StrandKind>, bytes: &'a [u8]) -> Self {
|
||||
match kind.into() {
|
||||
StrandKind::Os => Self::Os(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }),
|
||||
StrandKind::Utf8 => Self::Utf8(unsafe { str::from_utf8_unchecked(bytes) }),
|
||||
StrandKind::Os => Self::Os(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }),
|
||||
StrandKind::Bytes => Self::Bytes(bytes),
|
||||
}
|
||||
}
|
||||
|
|
@ -127,8 +164,8 @@ impl<'a> Strand<'a> {
|
|||
|
||||
pub fn kind(self) -> StrandKind {
|
||||
match self {
|
||||
Self::Os(_) => StrandKind::Os,
|
||||
Self::Utf8(_) => StrandKind::Utf8,
|
||||
Self::Os(_) => StrandKind::Os,
|
||||
Self::Bytes(_) => StrandKind::Bytes,
|
||||
}
|
||||
}
|
||||
|
|
@ -152,4 +189,17 @@ impl<'a> Strand<'a> {
|
|||
}
|
||||
|
||||
pub fn to_string_lossy(self) -> Cow<'a, str> { String::from_utf8_lossy(self.encoded_bytes()) }
|
||||
|
||||
pub fn with<K, S>(kind: K, strand: &'a S) -> Result<Self>
|
||||
where
|
||||
K: Into<StrandKind>,
|
||||
S: ?Sized + AsStrand,
|
||||
{
|
||||
let strand = strand.as_strand();
|
||||
Ok(match kind.into() {
|
||||
StrandKind::Utf8 => Self::Utf8(strand.as_utf8()?),
|
||||
StrandKind::Os => Self::Os(strand.as_os()?),
|
||||
StrandKind::Bytes => Self::Bytes(strand.encoded_bytes()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,10 @@
|
|||
use std::ffi::OsStr;
|
||||
|
||||
use crate::{path::PathDyn, strand::Strand};
|
||||
|
||||
// --- AsStrandView
|
||||
pub trait AsStrandView<'a, T> {
|
||||
fn as_strand_view(self) -> T;
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, &'a OsStr> for &'a str {
|
||||
fn as_strand_view(self) -> &'a OsStr { OsStr::new(self) }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, &'a OsStr> for &'a OsStr {
|
||||
fn as_strand_view(self) -> &'a OsStr { self }
|
||||
}
|
||||
|
|
@ -22,23 +16,3 @@ impl<'a> AsStrandView<'a, &'a OsStr> for &'a std::path::Path {
|
|||
impl<'a> AsStrandView<'a, &'a OsStr> for std::path::Components<'a> {
|
||||
fn as_strand_view(self) -> &'a OsStr { self.as_path().as_os_str() }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, Strand<'a>> for &'a str {
|
||||
fn as_strand_view(self) -> Strand<'a> { Strand::Utf8(self) }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, Strand<'a>> for &'a std::path::Path {
|
||||
fn as_strand_view(self) -> Strand<'a> { Strand::Os(self.as_os_str()) }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, Strand<'a>> for std::path::Components<'a> {
|
||||
fn as_strand_view(self) -> Strand<'a> { Strand::Os(self.as_path().as_os_str()) }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, Strand<'a>> for PathDyn<'a> {
|
||||
fn as_strand_view(self) -> Strand<'a> {
|
||||
match self {
|
||||
Self::Os(p) => Strand::Os(p.as_os_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}};
|
||||
|
||||
use crate::url::{UrlBuf, UrlLike};
|
||||
|
||||
pub trait IntoStringLossy {
|
||||
fn into_string_lossy(self) -> String;
|
||||
}
|
||||
|
||||
impl IntoStringLossy for String {
|
||||
fn into_string_lossy(self) -> String { self }
|
||||
}
|
||||
|
||||
impl IntoStringLossy for &OsStr {
|
||||
fn into_string_lossy(self) -> String { self.to_string_lossy().into_owned() }
|
||||
}
|
||||
|
||||
impl IntoStringLossy for OsString {
|
||||
fn into_string_lossy(self) -> String {
|
||||
match self.to_string_lossy() {
|
||||
Cow::Owned(s) => s,
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(self.into_encoded_bytes()) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStringLossy for Cow<'_, OsStr> {
|
||||
fn into_string_lossy(self) -> String {
|
||||
match self {
|
||||
Cow::Owned(s) => s.into_string_lossy(),
|
||||
Cow::Borrowed(s) => s.into_string_lossy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStringLossy for &UrlBuf {
|
||||
fn into_string_lossy(self) -> String { self.os_str().into_string_lossy() }
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, fmt::{Debug, Formatter}, path::{Path, PathBuf}, str::From
|
|||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{loc::LocBuf, path::{PathBufDyn, PathBufDynError, PathDyn, PathDynError, SetNameError}, pool::{InternStr, Pool, Symbol}, 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)]
|
||||
pub enum UrlBuf {
|
||||
|
|
@ -53,14 +53,6 @@ impl From<&Path> for UrlBuf {
|
|||
fn from(path: &Path) -> Self { path.to_path_buf().into() }
|
||||
}
|
||||
|
||||
impl From<PathDyn<'_>> for UrlBuf {
|
||||
fn from(path: PathDyn) -> Self {
|
||||
match path {
|
||||
PathDyn::Os(p) => p.to_path_buf().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UrlBuf {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
|
|
@ -75,6 +67,14 @@ impl TryFrom<String> for UrlBuf {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(Scheme, PathBufDyn)> for UrlBuf {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: (Scheme, PathBufDyn)) -> Result<Self, Self::Error> {
|
||||
Ok(UrlCow::try_from(value)?.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Self> for UrlBuf {
|
||||
fn as_ref(&self) -> &Self { self }
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ impl UrlBuf {
|
|||
pub fn to_regular(&self) -> Result<Self, PathDynError> { Ok(self.as_url().as_regular()?.into()) }
|
||||
|
||||
#[inline]
|
||||
pub fn into_regular(self) -> Result<Self, PathBufDynError> {
|
||||
pub fn into_regular(self) -> Result<Self, PathDynError> {
|
||||
Ok(Self::Regular(self.into_loc().into_os()?.into()))
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ impl UrlBuf {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_search(self, domain: impl AsRef<str>) -> Result<Self, PathBufDynError> {
|
||||
pub fn into_search(self, domain: impl AsRef<str>) -> Result<Self, PathDynError> {
|
||||
Ok(Self::Search {
|
||||
loc: LocBuf::<PathBuf>::zeroed(self.into_loc().into_os()?),
|
||||
domain: Pool::<str>::intern(domain),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, iter::FusedIterator, ops::Not, path::{self, PathBuf, PrefixComponent}};
|
||||
use std::path::PrefixComponent;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{path::{PathBufDyn, PathCow}, scheme::SchemeRef, url::{Encode, Url, UrlBuf, UrlCow}};
|
||||
use crate::{path::{self, PathDynError}, scheme::SchemeRef, strand::Strand};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Component<'a> {
|
||||
|
|
@ -11,171 +11,67 @@ pub enum Component<'a> {
|
|||
RootDir,
|
||||
CurDir,
|
||||
ParentDir,
|
||||
Normal(&'a OsStr),
|
||||
Normal(Strand<'a>),
|
||||
}
|
||||
|
||||
impl<'a> From<path::Component<'a>> for Component<'a> {
|
||||
fn from(comp: path::Component<'a>) -> Self {
|
||||
match comp {
|
||||
path::Component::Prefix(p) => Component::Prefix(p),
|
||||
path::Component::RootDir => Component::RootDir,
|
||||
path::Component::CurDir => Component::CurDir,
|
||||
path::Component::ParentDir => Component::ParentDir,
|
||||
path::Component::Normal(s) => Component::Normal(s),
|
||||
fn from(value: path::Component<'a>) -> Self {
|
||||
match value {
|
||||
path::Component::Prefix(p) => Self::Prefix(p),
|
||||
path::Component::RootDir => Self::RootDir,
|
||||
path::Component::CurDir => Self::CurDir,
|
||||
path::Component::ParentDir => Self::ParentDir,
|
||||
path::Component::Normal(s) => Self::Normal(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Component<'a>> for Result<UrlBuf> {
|
||||
fn from_iter<I: IntoIterator<Item = Component<'a>>>(iter: I) -> Self {
|
||||
let mut buf = PathBuf::new();
|
||||
let mut scheme = None;
|
||||
iter.into_iter().for_each(|c| match c {
|
||||
Component::Scheme(s) => scheme = Some(s),
|
||||
Component::Prefix(p) => buf.push(path::Component::Prefix(p)),
|
||||
Component::RootDir => buf.push(path::Component::RootDir),
|
||||
Component::CurDir => buf.push(path::Component::CurDir),
|
||||
Component::ParentDir => buf.push(path::Component::ParentDir),
|
||||
Component::Normal(s) => buf.push(path::Component::Normal(s)),
|
||||
});
|
||||
|
||||
Ok(if let Some(s) = scheme {
|
||||
UrlCow::try_from((s, PathCow::Owned(PathBufDyn::Os(buf))))?.into_owned()
|
||||
} else {
|
||||
buf.into()
|
||||
impl<'a> Component<'a> {
|
||||
pub fn downgrade(self) -> Option<path::Component<'a>> {
|
||||
Some(match self {
|
||||
Self::Scheme(_) => None?,
|
||||
Self::Prefix(p) => path::Component::Prefix(p),
|
||||
Self::RootDir => path::Component::RootDir,
|
||||
Self::CurDir => path::Component::CurDir,
|
||||
Self::ParentDir => path::Component::ParentDir,
|
||||
Self::Normal(s) => path::Component::Normal(s),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Component<'a>> for PathBuf {
|
||||
// impl<'a> FromIterator<Component<'a>> for Result<UrlBuf> {
|
||||
// fn from_iter<I: IntoIterator<Item = Component<'a>>>(iter: I) -> Self {
|
||||
// let mut buf = PathBuf::new();
|
||||
// let mut scheme = None;
|
||||
// iter.into_iter().for_each(|c| match c {
|
||||
// Component::Scheme(s) => scheme = Some(s),
|
||||
// Component::Prefix(p) => buf.push(path::Component::Prefix(p)),
|
||||
// Component::RootDir => buf.push(path::Component::RootDir),
|
||||
// Component::CurDir => buf.push(path::Component::CurDir),
|
||||
// Component::ParentDir => buf.push(path::Component::ParentDir),
|
||||
// Component::Normal(s) => buf.push(path::Component::Normal(s)),
|
||||
// });
|
||||
|
||||
// Ok(if let Some(s) = scheme {
|
||||
// UrlCow::try_from((s, PathCow::Owned(PathBufDyn::Os(buf))))?.into_owned()
|
||||
// } else {
|
||||
// buf.into()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<'a> FromIterator<Component<'a>> for Result<std::path::PathBuf, PathDynError> {
|
||||
fn from_iter<I: IntoIterator<Item = Component<'a>>>(iter: I) -> Self {
|
||||
let mut buf = Self::new();
|
||||
iter.into_iter().for_each(|c| match c {
|
||||
Component::Scheme(_) => {}
|
||||
Component::Prefix(p) => buf.push(path::Component::Prefix(p)),
|
||||
Component::RootDir => buf.push(path::Component::RootDir),
|
||||
Component::CurDir => buf.push(path::Component::CurDir),
|
||||
Component::ParentDir => buf.push(path::Component::ParentDir),
|
||||
Component::Normal(s) => buf.push(path::Component::Normal(s)),
|
||||
});
|
||||
buf
|
||||
iter.into_iter().filter_map(|c| c.downgrade()).map(std::path::Component::try_from).collect()
|
||||
}
|
||||
}
|
||||
|
||||
// --- Components
|
||||
#[derive(Clone)]
|
||||
pub struct Components<'a> {
|
||||
inner: path::Components<'a>,
|
||||
url: Url<'a>,
|
||||
scheme_yielded: bool,
|
||||
}
|
||||
|
||||
impl<'a> From<Url<'a>> for Components<'a> {
|
||||
fn from(value: Url<'a>) -> Self {
|
||||
Self { inner: value.loc().components(), url: value, scheme_yielded: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Components<'a> {
|
||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||
let path = self.inner.as_path();
|
||||
if self.url.kind().is_local() || self.scheme_yielded {
|
||||
return path.as_os_str().into();
|
||||
}
|
||||
|
||||
let mut s = OsString::from(Encode(self.url).to_string());
|
||||
s.reserve_exact(path.as_os_str().len());
|
||||
s.push(path);
|
||||
s.into()
|
||||
}
|
||||
|
||||
pub fn covariant(&self, other: &Self) -> bool {
|
||||
match (self.scheme_yielded, other.scheme_yielded) {
|
||||
(false, false) => {}
|
||||
(true, true) if self.url.scheme().covariant(other.url.scheme()) => {}
|
||||
_ => return false,
|
||||
}
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Components<'a> {
|
||||
type Item = Component<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.scheme_yielded {
|
||||
self.scheme_yielded = true;
|
||||
Some(Component::Scheme(self.url.scheme()))
|
||||
} else {
|
||||
self.inner.next().map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (min, max) = self.inner.size_hint();
|
||||
let scheme = self.scheme_yielded.not() as usize;
|
||||
|
||||
(min + scheme, max.map(|n| n + scheme))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for Components<'a> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if let Some(comp) = self.inner.next_back() {
|
||||
Some(comp.into())
|
||||
} else if !self.scheme_yielded {
|
||||
self.scheme_yielded = true;
|
||||
Some(Component::Scheme(self.url.scheme()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FusedIterator for Components<'a> {}
|
||||
|
||||
impl<'a> PartialEq for Components<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Some(self.url.scheme()).filter(|_| !self.scheme_yielded)
|
||||
== Some(other.url.scheme()).filter(|_| !other.scheme_yielded)
|
||||
&& self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
// --- Tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use super::*;
|
||||
use crate::url::UrlLike;
|
||||
|
||||
#[test]
|
||||
fn test_collect() -> Result<()> {
|
||||
crate::init_tests();
|
||||
|
||||
let search: UrlBuf = "search://keyword//root/projects/yazi".parse()?;
|
||||
assert_eq!(search.uri(), "");
|
||||
assert_eq!(search.scheme(), SchemeRef::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
|
||||
let item = search.try_join("main.rs")?;
|
||||
assert_eq!(item.uri(), "main.rs");
|
||||
assert_eq!(item.scheme(), SchemeRef::Search { domain: "keyword", uri: 1, urn: 1 });
|
||||
|
||||
let u: UrlBuf = item.components().take(4).collect::<Result<_>>()?;
|
||||
assert_eq!(u.scheme(), SchemeRef::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
assert_eq!(u.loc(), Path::new("/root/projects"));
|
||||
|
||||
let u: UrlBuf = item
|
||||
.components()
|
||||
.take(5)
|
||||
.chain([Component::Normal(OsStr::new("target/release/yazi"))])
|
||||
.collect::<Result<_>>()?;
|
||||
assert_eq!(u.scheme(), SchemeRef::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
assert_eq!(u.loc(), Path::new("/root/projects/yazi/target/release/yazi"));
|
||||
|
||||
Ok(())
|
||||
impl<'a> FromIterator<Component<'a>> for Result<typed_path::UnixPathBuf, PathDynError> {
|
||||
fn from_iter<I: IntoIterator<Item = Component<'a>>>(iter: I) -> Self {
|
||||
iter
|
||||
.into_iter()
|
||||
.filter_map(|c| c.downgrade())
|
||||
.map(typed_path::UnixComponent::try_from)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
196
yazi-shared/src/url/components.rs
Normal file
196
yazi-shared/src/url/components.rs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, iter::FusedIterator, ops::Not};
|
||||
|
||||
use crate::{loc::Loc, path, scheme::{Encode as EncodeScheme, SchemeCow, SchemeRef}, strand::{StrandBuf, StrandCow}, url::{Component, Encode as EncodeUrl, Url}};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Components<'a> {
|
||||
inner: path::Components<'a>,
|
||||
url: Url<'a>,
|
||||
back_yields: usize,
|
||||
scheme_yielded: bool,
|
||||
}
|
||||
|
||||
impl<'a> From<Url<'a>> for Components<'a> {
|
||||
fn from(value: Url<'a>) -> Self {
|
||||
Self {
|
||||
inner: value.loc().components(),
|
||||
url: value,
|
||||
back_yields: 0,
|
||||
scheme_yielded: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Components<'a> {
|
||||
pub fn covariant(&self, other: &Self) -> bool {
|
||||
match (self.scheme_yielded, other.scheme_yielded) {
|
||||
(true, true) => {}
|
||||
(false, false) if self.scheme().covariant(other.scheme()) => {}
|
||||
_ => return false,
|
||||
}
|
||||
self.inner == other.inner
|
||||
}
|
||||
|
||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||
let Ok(path) = self.inner.path().as_os() else {
|
||||
return OsString::from(EncodeUrl(self.url()).to_string()).into();
|
||||
};
|
||||
|
||||
if self.url.is_regular() || self.scheme_yielded {
|
||||
return path.as_os_str().into();
|
||||
}
|
||||
|
||||
let mut s = OsString::from(EncodeScheme(self.url()).to_string());
|
||||
s.reserve_exact(path.as_os_str().len());
|
||||
s.push(path);
|
||||
s.into()
|
||||
}
|
||||
|
||||
pub fn scheme(&self) -> SchemeRef<'a> {
|
||||
let left = self.inner.clone().count();
|
||||
|
||||
let (uri, urn) = SchemeCow::retrieve_ports(self.url);
|
||||
let (uri, urn) = (
|
||||
uri.saturating_sub(self.back_yields).min(left),
|
||||
urn.saturating_sub(self.back_yields).min(left),
|
||||
);
|
||||
|
||||
match self.url {
|
||||
Url::Regular(_) => SchemeRef::Regular { uri, urn },
|
||||
Url::Search { domain, .. } => SchemeRef::Search { domain, uri, urn },
|
||||
Url::Archive { domain, .. } => SchemeRef::Archive { domain, uri, urn },
|
||||
Url::Sftp { domain, .. } => SchemeRef::Sftp { domain, uri, urn },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strand(&self) -> StrandCow<'a> {
|
||||
let s = self.inner.strand();
|
||||
if self.url.is_regular() || self.scheme_yielded {
|
||||
return s.into();
|
||||
}
|
||||
|
||||
let mut buf = StrandBuf::with_str(s.kind(), EncodeScheme(self.url()).to_string());
|
||||
buf.reserve_exact(s.len());
|
||||
buf.try_push(s).expect("strand with same kind");
|
||||
buf.into()
|
||||
}
|
||||
|
||||
pub fn url(&self) -> Url<'a> {
|
||||
let path = self.inner.path();
|
||||
let (uri, urn) = self.scheme().ports();
|
||||
match self.url {
|
||||
Url::Regular(_) => Url::Regular(Loc::with(path.as_os().unwrap(), uri, urn).unwrap()),
|
||||
Url::Search { domain, .. } => {
|
||||
Url::Search { loc: Loc::with(path.as_os().unwrap(), uri, urn).unwrap(), domain }
|
||||
}
|
||||
Url::Archive { domain, .. } => {
|
||||
Url::Archive { loc: Loc::with(path.as_os().unwrap(), uri, urn).unwrap(), domain }
|
||||
}
|
||||
Url::Sftp { domain, .. } => {
|
||||
Url::Sftp { loc: Loc::with(path.as_os().unwrap(), uri, urn).unwrap(), domain }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Components<'a> {
|
||||
type Item = Component<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.scheme_yielded {
|
||||
self.scheme_yielded = true;
|
||||
Some(Component::Scheme(self.scheme()))
|
||||
} else {
|
||||
self.inner.next().map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (min, max) = self.inner.size_hint();
|
||||
let scheme = self.scheme_yielded.not() as usize;
|
||||
|
||||
(min + scheme, max.map(|n| n + scheme))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for Components<'a> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if let Some(c) = self.inner.next_back() {
|
||||
self.back_yields += 1;
|
||||
Some(c.into())
|
||||
} else if !self.scheme_yielded {
|
||||
self.scheme_yielded = true;
|
||||
Some(Component::Scheme(self.scheme()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FusedIterator for Components<'a> {}
|
||||
|
||||
impl<'a> PartialEq for Components<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.inner != other.inner {
|
||||
return false;
|
||||
}
|
||||
match (self.scheme_yielded, other.scheme_yielded) {
|
||||
(true, true) => true,
|
||||
(false, false) if self.scheme() == other.scheme() => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{scheme::SchemeRef, url::{Component, UrlBuf, UrlLike}};
|
||||
|
||||
#[test]
|
||||
fn test_url() -> Result<()> {
|
||||
use Component::*;
|
||||
use SchemeRef as S;
|
||||
|
||||
crate::init_tests();
|
||||
|
||||
let search: UrlBuf = "search://keyword//root/projects/yazi".parse()?;
|
||||
assert_eq!(search.uri(), "");
|
||||
assert_eq!(search.scheme(), S::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
|
||||
let src = search.try_join("src")?;
|
||||
assert_eq!(src.uri(), "src");
|
||||
assert_eq!(src.scheme(), S::Search { domain: "keyword", uri: 1, urn: 1 });
|
||||
|
||||
let main = src.try_join("main.rs")?;
|
||||
assert_eq!(main.urn(), "src/main.rs");
|
||||
assert_eq!(main.scheme(), S::Search { domain: "keyword", uri: 2, urn: 2 });
|
||||
|
||||
let mut it = main.components();
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 2, urn: 2 });
|
||||
assert_eq!(it.next_back(), Some(Normal("main.rs".into())));
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 1, urn: 1 });
|
||||
assert_eq!(it.next_back(), Some(Normal("src".into())));
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
assert_eq!(it.next_back(), Some(Normal("yazi".into())));
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
|
||||
let mut it = main.components();
|
||||
assert_eq!(it.next(), Some(Scheme(S::Search { domain: "keyword", uri: 2, urn: 2 })));
|
||||
assert_eq!(it.next(), Some(RootDir));
|
||||
assert_eq!(it.next(), Some(Normal("root".into())));
|
||||
assert_eq!(it.next(), Some(Normal("projects".into())));
|
||||
assert_eq!(it.next(), Some(Normal("yazi".into())));
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 2, urn: 2 });
|
||||
assert_eq!(it.next(), Some(Normal("src".into())));
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 1, urn: 1 });
|
||||
assert_eq!(it.next_back(), Some(Normal("main.rs".into())));
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.url().scheme(), S::Search { domain: "keyword", uri: 0, urn: 0 });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,12 @@
|
|||
use crate::{scheme::Encode, url::Url};
|
||||
|
||||
pub struct Display<'a> {
|
||||
inner: Url<'a>,
|
||||
}
|
||||
pub struct Display<'a>(pub Url<'a>);
|
||||
|
||||
impl<'a> From<Url<'a>> for Display<'a> {
|
||||
fn from(value: Url<'a>) -> Self { Self { inner: value } }
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Display for Display<'a> {
|
||||
impl std::fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (kind, loc) = (self.inner.kind(), self.inner.loc());
|
||||
let (kind, loc) = (self.0.kind(), self.0.loc());
|
||||
if kind.is_virtual() {
|
||||
Encode(self.inner).fmt(f)?;
|
||||
Encode(self.0).fmt(f)?;
|
||||
}
|
||||
loc.display().fmt(f)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use percent_encoding::{CONTROLS, percent_encode};
|
|||
|
||||
use crate::url::Url;
|
||||
|
||||
// --- Tilded
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Encode<'a>(pub Url<'a>);
|
||||
|
||||
|
|
|
|||
75
yazi-shared/src/url/like.rs
Normal file
75
yazi-shared/src/url/like.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{path::{AsPathRef, EndsWithError, JoinError, PathDyn, StartsWithError, StripPrefixError}, scheme::{SchemeKind, SchemeRef}, strand::{AsStrand, Strand}, url::{AsUrl, Components, Display, Url, UrlBuf, UrlCow}};
|
||||
|
||||
pub trait UrlLike
|
||||
where
|
||||
Self: AsUrl,
|
||||
{
|
||||
fn as_local(&self) -> Option<&Path> { self.as_url().as_local() }
|
||||
|
||||
fn base(&self) -> Url<'_> { self.as_url().base() }
|
||||
|
||||
fn components(&self) -> Components<'_> { self.as_url().into() }
|
||||
|
||||
fn covariant(&self, other: impl AsUrl) -> bool { self.as_url().covariant(other) }
|
||||
|
||||
fn display(&self) -> Display<'_> { Display(self.as_url()) }
|
||||
|
||||
fn ext(&self) -> Option<Strand<'_>> { self.as_url().ext() }
|
||||
|
||||
fn has_base(&self) -> bool { self.as_url().has_base() }
|
||||
|
||||
fn has_root(&self) -> bool { self.as_url().has_root() }
|
||||
|
||||
fn has_trail(&self) -> bool { self.as_url().has_trail() }
|
||||
|
||||
fn is_absolute(&self) -> bool { self.as_url().is_absolute() }
|
||||
|
||||
fn kind(&self) -> SchemeKind { self.as_url().kind() }
|
||||
|
||||
fn loc(&self) -> PathDyn<'_> { self.as_url().loc() }
|
||||
|
||||
fn name(&self) -> Option<Strand<'_>> { self.as_url().name() }
|
||||
|
||||
fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||
|
||||
fn pair(&self) -> Option<(Url<'_>, PathDyn<'_>)> { self.as_url().pair() }
|
||||
|
||||
fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
|
||||
|
||||
fn scheme(&self) -> SchemeRef<'_> { self.as_url().scheme() }
|
||||
|
||||
fn stem(&self) -> Option<Strand<'_>> { self.as_url().stem() }
|
||||
|
||||
fn trail(&self) -> Url<'_> { self.as_url().trail() }
|
||||
|
||||
fn try_ends_with(&self, child: impl AsUrl) -> Result<bool, EndsWithError> {
|
||||
self.as_url().try_ends_with(child)
|
||||
}
|
||||
|
||||
fn try_join(&self, path: impl AsStrand) -> Result<UrlBuf, JoinError> {
|
||||
self.as_url().try_join(path)
|
||||
}
|
||||
|
||||
fn try_replace<'a>(&self, take: usize, path: impl AsPathRef<'a>) -> Result<UrlCow<'a>> {
|
||||
self.as_url().try_replace(take, path)
|
||||
}
|
||||
|
||||
fn try_starts_with(&self, base: impl AsUrl) -> Result<bool, StartsWithError> {
|
||||
self.as_url().try_starts_with(base)
|
||||
}
|
||||
|
||||
fn try_strip_prefix(&self, base: impl AsUrl) -> Result<PathDyn<'_>, StripPrefixError> {
|
||||
self.as_url().try_strip_prefix(base)
|
||||
}
|
||||
|
||||
fn uri(&self) -> PathDyn<'_> { self.as_url().uri() }
|
||||
|
||||
fn urn(&self) -> PathDyn<'_> { self.as_url().urn() }
|
||||
}
|
||||
|
||||
impl UrlLike for UrlBuf {}
|
||||
impl UrlLike for UrlCow<'_> {}
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(buf component cov cow display encode traits url);
|
||||
yazi_macro::mod_flat!(buf component components cov cow display encode like traits url);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{loc::Loc, path::{AsPathRef, EndsWithError, JoinError, PathDyn, StartsWithError, StripPrefixError}, scheme::{SchemeKind, SchemeRef}, strand::{AsStrand, Strand}, url::{Components, Display, Url, UrlBuf, UrlCow}};
|
||||
use crate::{loc::Loc, url::{Url, UrlBuf, UrlCow}};
|
||||
|
||||
// --- AsUrl
|
||||
pub trait AsUrl {
|
||||
|
|
@ -29,15 +27,6 @@ impl AsUrl for &PathBuf {
|
|||
fn as_url(&self) -> Url<'_> { (*self).as_path().as_url() }
|
||||
}
|
||||
|
||||
impl AsUrl for PathDyn<'_> {
|
||||
#[inline]
|
||||
fn as_url(&self) -> Url<'_> {
|
||||
match *self {
|
||||
Self::Os(p) => p.as_url(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsUrl for Url<'_> {
|
||||
#[inline]
|
||||
fn as_url(&self) -> Url<'_> { *self }
|
||||
|
|
@ -82,10 +71,13 @@ impl AsUrl for UrlCow<'_> {
|
|||
}
|
||||
|
||||
impl AsUrl for &UrlCow<'_> {
|
||||
#[inline]
|
||||
fn as_url(&self) -> Url<'_> { (**self).as_url() }
|
||||
}
|
||||
|
||||
impl AsUrl for super::Components<'_> {
|
||||
fn as_url(&self) -> Url<'_> { self.url() }
|
||||
}
|
||||
|
||||
impl<'a, T> From<&'a T> for Url<'a>
|
||||
where
|
||||
T: AsUrl + ?Sized,
|
||||
|
|
@ -99,74 +91,3 @@ where
|
|||
{
|
||||
fn from(value: &'a mut T) -> Self { value.as_url() }
|
||||
}
|
||||
|
||||
// UrlLike
|
||||
pub trait UrlLike
|
||||
where
|
||||
Self: AsUrl,
|
||||
{
|
||||
fn as_local(&self) -> Option<&Path> { self.as_url().as_local() }
|
||||
|
||||
fn base(&self) -> Url<'_> { self.as_url().base() }
|
||||
|
||||
fn components(&self) -> Components<'_> { self.as_url().into() }
|
||||
|
||||
fn covariant(&self, other: impl AsUrl) -> bool { self.as_url().covariant(other) }
|
||||
|
||||
fn display(&self) -> Display<'_> { self.as_url().into() }
|
||||
|
||||
fn ext(&self) -> Option<Strand<'_>> { self.as_url().ext() }
|
||||
|
||||
fn has_base(&self) -> bool { self.as_url().has_base() }
|
||||
|
||||
fn has_root(&self) -> bool { self.as_url().has_root() }
|
||||
|
||||
fn has_trail(&self) -> bool { self.as_url().has_trail() }
|
||||
|
||||
fn is_absolute(&self) -> bool { self.as_url().is_absolute() }
|
||||
|
||||
fn kind(&self) -> SchemeKind { self.as_url().kind() }
|
||||
|
||||
fn loc(&self) -> PathDyn<'_> { self.as_url().loc() }
|
||||
|
||||
fn name(&self) -> Option<Strand<'_>> { self.as_url().name() }
|
||||
|
||||
fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||
|
||||
fn pair(&self) -> Option<(Url<'_>, PathDyn<'_>)> { self.as_url().pair() }
|
||||
|
||||
fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
|
||||
|
||||
fn scheme(&self) -> SchemeRef<'_> { self.as_url().scheme() }
|
||||
|
||||
fn stem(&self) -> Option<Strand<'_>> { self.as_url().stem() }
|
||||
|
||||
fn trail(&self) -> Url<'_> { self.as_url().trail() }
|
||||
|
||||
fn try_ends_with(&self, child: impl AsUrl) -> Result<bool, EndsWithError> {
|
||||
self.as_url().try_ends_with(child)
|
||||
}
|
||||
|
||||
fn try_join(&self, path: impl AsStrand) -> Result<UrlBuf, JoinError> {
|
||||
self.as_url().try_join(path)
|
||||
}
|
||||
|
||||
fn try_replace<'a>(&self, take: usize, path: impl AsPathRef<'a>) -> Result<UrlCow<'a>> {
|
||||
self.as_url().try_replace(take, path)
|
||||
}
|
||||
|
||||
fn try_starts_with(&self, base: impl AsUrl) -> Result<bool, StartsWithError> {
|
||||
self.as_url().try_starts_with(base)
|
||||
}
|
||||
|
||||
fn try_strip_prefix(&self, base: impl AsUrl) -> Result<PathDyn<'_>, StripPrefixError> {
|
||||
self.as_url().try_strip_prefix(base)
|
||||
}
|
||||
|
||||
fn uri(&self) -> PathDyn<'_> { self.as_url().uri() }
|
||||
|
||||
fn urn(&self) -> PathDyn<'_> { self.as_url().urn() }
|
||||
}
|
||||
|
||||
impl UrlLike for UrlBuf {}
|
||||
impl UrlLike for UrlCow<'_> {}
|
||||
|
|
|
|||
|
|
@ -39,15 +39,85 @@ impl Debug for Url<'_> {
|
|||
impl Serialize for Url<'_> {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let (kind, loc) = (self.kind(), self.loc());
|
||||
match (kind.is_virtual(), loc.to_str()) {
|
||||
(false, Ok(s)) => serializer.serialize_str(s),
|
||||
(true, Ok(s)) => serializer.serialize_str(&format!("{}{s}", EncodeScheme(*self))),
|
||||
match (kind == SchemeKind::Regular, loc.to_str()) {
|
||||
(true, Ok(s)) => serializer.serialize_str(s),
|
||||
(false, Ok(s)) => serializer.serialize_str(&format!("{}{s}", EncodeScheme(*self))),
|
||||
(_, Err(_)) => serializer.collect_str(&EncodeUrl(*self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Url<'a> {
|
||||
#[inline]
|
||||
pub fn as_local(self) -> Option<&'a Path> {
|
||||
self.loc().as_os().ok().filter(|_| self.kind().is_local())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_regular(self) -> Result<Self, PathDynError> {
|
||||
Ok(Self::Regular(Loc::bare(self.loc().as_os()?)))
|
||||
}
|
||||
|
||||
pub fn base(self) -> Self {
|
||||
match self {
|
||||
Self::Regular(loc) => Self::Regular(Loc::bare(loc.base())),
|
||||
Self::Search { loc, domain } => Self::Search { loc: Loc::bare(loc.base()), domain },
|
||||
Self::Archive { loc, domain } => Self::Archive { loc: Loc::bare(loc.base()), domain },
|
||||
Self::Sftp { loc, domain } => Self::Sftp { loc: Loc::bare(loc.base()), domain },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn components(self) -> Components<'a> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(self, other: impl AsUrl) -> bool {
|
||||
let other = other.as_url();
|
||||
self.loc() == other.loc() && self.scheme().covariant(other.scheme())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ext(self) -> Option<Strand<'a>> {
|
||||
Some(match self {
|
||||
Self::Regular(loc) => loc.extension()?.as_strand(),
|
||||
Self::Search { loc, .. } => loc.extension()?.as_strand(),
|
||||
Self::Archive { loc, .. } => loc.extension()?.as_strand(),
|
||||
Self::Sftp { loc, .. } => loc.extension()?.as_strand(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_base(self) -> bool {
|
||||
match self {
|
||||
Self::Regular(loc) => loc.has_base(),
|
||||
Self::Search { loc, .. } => loc.has_base(),
|
||||
Self::Archive { loc, .. } => loc.has_base(),
|
||||
Self::Sftp { loc, .. } => loc.has_base(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_root(self) -> bool { self.loc().has_root() }
|
||||
|
||||
#[inline]
|
||||
pub fn has_trail(self) -> bool {
|
||||
match self {
|
||||
Self::Regular(loc) => loc.has_trail(),
|
||||
Self::Search { loc, .. } => loc.has_trail(),
|
||||
Self::Archive { loc, .. } => loc.has_trail(),
|
||||
Self::Sftp { loc, .. } => loc.has_trail(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_absolute(self) -> bool { self.loc().is_absolute() }
|
||||
|
||||
#[inline]
|
||||
pub fn is_regular(self) -> bool { matches!(self, Self::Regular(_)) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_search(self) -> bool { matches!(self, Self::Search { .. }) }
|
||||
|
||||
#[inline]
|
||||
pub fn kind(&self) -> SchemeKind {
|
||||
match self {
|
||||
|
|
@ -68,6 +138,54 @@ impl<'a> Url<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name(self) -> Option<Strand<'a>> {
|
||||
Some(match self {
|
||||
Self::Regular(loc) => loc.file_name()?.as_strand(),
|
||||
Self::Search { loc, .. } => loc.file_name()?.as_strand(),
|
||||
Self::Archive { loc, .. } => loc.file_name()?.as_strand(),
|
||||
Self::Sftp { loc, .. } => loc.file_name()?.as_strand(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn os_str(self) -> Cow<'a, OsStr> { self.components().os_str() }
|
||||
|
||||
#[inline]
|
||||
pub fn pair(self) -> Option<(Self, PathDyn<'a>)> { Some((self.parent()?, self.urn())) }
|
||||
|
||||
pub fn parent(self) -> Option<Self> {
|
||||
let uri = self.uri();
|
||||
|
||||
Some(match self {
|
||||
// Regular
|
||||
Self::Regular(loc) => Self::regular(loc.parent()?),
|
||||
|
||||
// Search
|
||||
Self::Search { loc, .. } if uri.is_empty() => Self::regular(loc.parent()?),
|
||||
Self::Search { loc, domain } => {
|
||||
Self::Search { loc: Loc::new(loc.parent()?, loc.base(), loc.base()), domain }
|
||||
}
|
||||
|
||||
// Archive
|
||||
Self::Archive { loc, .. } if uri.is_empty() => Self::regular(loc.parent()?),
|
||||
Self::Archive { loc, domain } if uri.components().nth(1).is_none() => {
|
||||
Self::Archive { loc: Loc::zeroed(loc.parent()?), domain }
|
||||
}
|
||||
Self::Archive { loc, domain } => {
|
||||
Self::Archive { loc: Loc::floated(loc.parent()?, loc.base()), domain }
|
||||
}
|
||||
|
||||
// SFTP
|
||||
Self::Sftp { loc, domain } => Self::Sftp { loc: Loc::bare(loc.parent()?), domain },
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn regular<T: AsRef<Path> + ?Sized>(path: &'a T) -> Self {
|
||||
Self::Regular(Loc::bare(path.as_ref()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scheme(self) -> SchemeRef<'a> {
|
||||
let (uri, urn) = SchemeCow::retrieve_ports(self);
|
||||
|
|
@ -80,30 +198,47 @@ impl<'a> Url<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn regular<T: AsRef<Path> + ?Sized>(path: &'a T) -> Self {
|
||||
Self::Regular(Loc::bare(path.as_ref()))
|
||||
pub fn stem(self) -> Option<Strand<'a>> {
|
||||
Some(match self {
|
||||
Self::Regular(loc) => loc.file_stem()?.as_strand(),
|
||||
Self::Search { loc, .. } => loc.file_stem()?.as_strand(),
|
||||
Self::Archive { loc, .. } => loc.file_stem()?.as_strand(),
|
||||
Self::Sftp { loc, .. } => loc.file_stem()?.as_strand(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_regular(self) -> bool { matches!(self, Self::Regular(_)) }
|
||||
|
||||
#[inline]
|
||||
pub fn as_regular(self) -> Result<Self, PathDynError> {
|
||||
Ok(Self::Regular(Loc::bare(self.loc().as_os()?)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_search(self) -> bool { matches!(self, Self::Search { .. }) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_absolute(self) -> bool { self.loc().is_absolute() }
|
||||
|
||||
#[inline]
|
||||
pub fn has_root(self) -> bool { self.loc().has_root() }
|
||||
|
||||
#[inline]
|
||||
pub fn to_owned(self) -> UrlBuf { self.into() }
|
||||
|
||||
#[inline]
|
||||
pub fn trail(self) -> Self {
|
||||
match self {
|
||||
Self::Regular(loc) => Self::Regular(Loc::bare(loc.trail())),
|
||||
Self::Search { loc, domain } => Self::Search { loc: Loc::bare(loc.trail()), domain },
|
||||
Self::Archive { loc, domain } => Self::Archive { loc: Loc::bare(loc.trail()), domain },
|
||||
Self::Sftp { loc, domain } => Self::Sftp { loc: Loc::bare(loc.trail()), domain },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn triple(self) -> (PathDyn<'a>, PathDyn<'a>, PathDyn<'a>) {
|
||||
match self {
|
||||
Self::Regular(loc) | Self::Search { loc, .. } | Self::Archive { loc, .. } => {
|
||||
let (base, rest, urn) = loc.triple();
|
||||
(base.as_path(), rest.as_path(), urn.as_path())
|
||||
}
|
||||
Self::Sftp { loc, .. } => {
|
||||
let (base, rest, urn) = loc.triple();
|
||||
(base.as_path(), rest.as_path(), urn.as_path())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_ends_with(self, child: impl AsUrl) -> Result<bool, EndsWithError> {
|
||||
let child = child.as_url();
|
||||
Ok(self.loc().try_ends_with(child.loc())? && self.scheme().covariant(child.scheme()))
|
||||
}
|
||||
|
||||
pub fn try_join(self, path: impl AsStrand) -> Result<UrlBuf, JoinError> {
|
||||
let joined = self.loc().try_join(path)?;
|
||||
|
||||
|
|
@ -135,7 +270,8 @@ impl<'a> Url<'a> {
|
|||
return UrlCow::try_from(b);
|
||||
}
|
||||
|
||||
let mut path = PathBufDyn::Os(self.loc().components().take(take - 1).collect()); // FIXME
|
||||
let loc = self.loc();
|
||||
let mut path = PathBufDyn::from_components(loc.kind(), loc.components().take(take - 1))?;
|
||||
path.try_push(rep)?;
|
||||
|
||||
let url = match self {
|
||||
|
|
@ -171,6 +307,12 @@ impl<'a> Url<'a> {
|
|||
Ok(url.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_starts_with(self, base: impl AsUrl) -> Result<bool, StartsWithError> {
|
||||
let base = base.as_url();
|
||||
Ok(self.loc().try_starts_with(base.loc())? && self.scheme().covariant(base.scheme()))
|
||||
}
|
||||
|
||||
pub fn try_strip_prefix(self, base: impl AsUrl) -> Result<PathDyn<'a>, StripPrefixError> {
|
||||
use StripPrefixError::{Exotic, NotPrefix};
|
||||
use Url as U;
|
||||
|
|
@ -236,145 +378,4 @@ impl<'a> Url<'a> {
|
|||
Self::Sftp { loc, .. } => loc.urn().as_path(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name(self) -> Option<Strand<'a>> {
|
||||
Some(match self {
|
||||
Self::Regular(loc) => loc.file_name()?.as_strand(),
|
||||
Self::Search { loc, .. } => loc.file_name()?.as_strand(),
|
||||
Self::Archive { loc, .. } => loc.file_name()?.as_strand(),
|
||||
Self::Sftp { loc, .. } => loc.file_name()?.as_strand(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn stem(self) -> Option<Strand<'a>> {
|
||||
Some(match self {
|
||||
Self::Regular(loc) => loc.file_stem()?.as_strand(),
|
||||
Self::Search { loc, .. } => loc.file_stem()?.as_strand(),
|
||||
Self::Archive { loc, .. } => loc.file_stem()?.as_strand(),
|
||||
Self::Sftp { loc, .. } => loc.file_stem()?.as_strand(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ext(self) -> Option<Strand<'a>> {
|
||||
Some(match self {
|
||||
Self::Regular(loc) => loc.extension()?.as_strand(),
|
||||
Self::Search { loc, .. } => loc.extension()?.as_strand(),
|
||||
Self::Archive { loc, .. } => loc.extension()?.as_strand(),
|
||||
Self::Sftp { loc, .. } => loc.extension()?.as_strand(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn base(self) -> Self {
|
||||
match self {
|
||||
Self::Regular(loc) => Self::Regular(Loc::bare(loc.base())),
|
||||
Self::Search { loc, domain } => Self::Search { loc: Loc::bare(loc.base()), domain },
|
||||
Self::Archive { loc, domain } => Self::Archive { loc: Loc::bare(loc.base()), domain },
|
||||
Self::Sftp { loc, domain } => Self::Sftp { loc: Loc::bare(loc.base()), domain },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn trail(self) -> Self {
|
||||
match self {
|
||||
Self::Regular(loc) => Self::Regular(Loc::bare(loc.trail())),
|
||||
Self::Search { loc, domain } => Self::Search { loc: Loc::bare(loc.trail()), domain },
|
||||
Self::Archive { loc, domain } => Self::Archive { loc: Loc::bare(loc.trail()), domain },
|
||||
Self::Sftp { loc, domain } => Self::Sftp { loc: Loc::bare(loc.trail()), domain },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn triple(self) -> (PathDyn<'a>, PathDyn<'a>, PathDyn<'a>) {
|
||||
match self {
|
||||
Self::Regular(loc) | Self::Search { loc, .. } | Self::Archive { loc, .. } => {
|
||||
let (base, rest, urn) = loc.triple();
|
||||
(base.as_path(), rest.as_path(), urn.as_path())
|
||||
}
|
||||
Self::Sftp { loc, .. } => {
|
||||
let (base, rest, urn) = loc.triple();
|
||||
(base.as_path(), rest.as_path(), urn.as_path())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(self) -> Option<Self> {
|
||||
let uri = self.uri();
|
||||
|
||||
Some(match self {
|
||||
// Regular
|
||||
Self::Regular(loc) => Self::regular(loc.parent()?),
|
||||
|
||||
// Search
|
||||
Self::Search { loc, .. } if uri.is_empty() => Self::regular(loc.parent()?),
|
||||
Self::Search { loc, domain } => {
|
||||
Self::Search { loc: Loc::new(loc.parent()?, loc.base(), loc.base()), domain }
|
||||
}
|
||||
|
||||
// Archive
|
||||
Self::Archive { loc, .. } if uri.is_empty() => Self::regular(loc.parent()?),
|
||||
Self::Archive { loc, domain } if uri.components().nth(1).is_none() => {
|
||||
Self::Archive { loc: Loc::zeroed(loc.parent()?), domain }
|
||||
}
|
||||
Self::Archive { loc, domain } => {
|
||||
Self::Archive { loc: Loc::floated(loc.parent()?, loc.base()), domain }
|
||||
}
|
||||
|
||||
// SFTP
|
||||
Self::Sftp { loc, domain } => Self::Sftp { loc: Loc::bare(loc.parent()?), domain },
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_starts_with(self, base: impl AsUrl) -> Result<bool, StartsWithError> {
|
||||
let base = base.as_url();
|
||||
Ok(self.loc().try_starts_with(base.loc())? && self.scheme().covariant(base.scheme()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_ends_with(self, child: impl AsUrl) -> Result<bool, EndsWithError> {
|
||||
let child = child.as_url();
|
||||
Ok(self.loc().try_ends_with(child.loc())? && self.scheme().covariant(child.scheme()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn components(self) -> Components<'a> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn os_str(self) -> Cow<'a, OsStr> { self.components().os_str() }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(self, other: impl AsUrl) -> bool {
|
||||
let other = other.as_url();
|
||||
self.loc() == other.loc() && self.scheme().covariant(other.scheme())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pair(self) -> Option<(Self, PathDyn<'a>)> { Some((self.parent()?, self.urn())) }
|
||||
|
||||
#[inline]
|
||||
pub fn as_local(self) -> Option<&'a Path> {
|
||||
self.loc().as_os().ok().filter(|_| self.kind().is_local())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_base(self) -> bool {
|
||||
match self {
|
||||
Self::Regular(loc) => loc.has_base(),
|
||||
Self::Search { loc, .. } => loc.has_base(),
|
||||
Self::Archive { loc, .. } => loc.has_base(),
|
||||
Self::Sftp { loc, .. } => loc.has_base(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_trail(self) -> bool {
|
||||
match self {
|
||||
Self::Regular(loc) => loc.has_trail(),
|
||||
Self::Search { loc, .. } => loc.has_trail(),
|
||||
Self::Archive { loc, .. } => loc.has_trail(),
|
||||
Self::Sftp { loc, .. } => loc.has_trail(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ async fn _unique_name(mut url: UrlBuf, append: bool) -> io::Result<UrlBuf> {
|
|||
let dot_ext = match url.ext() {
|
||||
Some(e) => {
|
||||
let mut s = StrandBuf::with_capacity(url.kind(), e.len() + 1);
|
||||
s.try_push(".")?;
|
||||
s.push_str(".");
|
||||
s.try_push(e)?;
|
||||
s
|
||||
}
|
||||
|
|
@ -53,9 +53,9 @@ async fn _unique_name(mut url: UrlBuf, append: bool) -> io::Result<UrlBuf> {
|
|||
|
||||
if append {
|
||||
name.try_push(&dot_ext)?;
|
||||
name.try_push(format!("_{i}"))?;
|
||||
name.push_str(format!("_{i}"));
|
||||
} else {
|
||||
name.try_push(format!("_{i}"))?;
|
||||
name.push_str(format!("_{i}"));
|
||||
name.try_push(&dot_ext)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use yazi_shared::{path::{AsPath, PathBufDyn}, pool::InternStr, url::{Url, UrlBuf
|
|||
use super::Cha;
|
||||
use crate::provider::sftp::Conn;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
pub struct Sftp<'a> {
|
||||
url: Url<'a>,
|
||||
path: &'a Path,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue