diff --git a/Cargo.lock b/Cargo.lock index 1fc44867..ecea9b47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -364,9 +364,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "chrono" @@ -1967,9 +1967,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.38.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" +checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8" dependencies = [ "memchr", ] @@ -2153,7 +2153,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -2574,11 +2574,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.15", + "thiserror-impl 2.0.16", ] [[package]] @@ -2594,9 +2594,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -3153,11 +3153,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] diff --git a/yazi-actor/src/mgr/cd.rs b/yazi-actor/src/mgr/cd.rs index 09f65b1c..b008a929 100644 --- a/yazi-actor/src/mgr/cd.rs +++ b/yazi-actor/src/mgr/cd.rs @@ -78,13 +78,13 @@ impl Cd { Ok(s) => { let Ok(url) = UrlBuf::try_from(s).map(expand_url) else { return }; - let Ok(file) = File::new(url.clone()).await else { return }; + let Ok(file) = File::new(&url).await else { return }; if file.is_dir() { return MgrProxy::cd(&url); } if let Some(p) = url.parent_url() { - FilesOp::Upserting(p, [(url.urn_owned(), file)].into()).emit(); + FilesOp::Upserting(p.into(), [(url.urn_owned(), file)].into()).emit(); } MgrProxy::reveal(&url); } diff --git a/yazi-actor/src/mgr/create.rs b/yazi-actor/src/mgr/create.rs index 1f7a620f..038fb362 100644 --- a/yazi-actor/src/mgr/create.rs +++ b/yazi-actor/src/mgr/create.rs @@ -48,7 +48,7 @@ impl Create { provider::create_dir_all(&new).await?; } else if let Some(real) = realname(&new).await { ok_or_not_found(provider::remove_file(&new).await)?; - FilesOp::Deleting(parent.clone(), [UrnBuf::from(real)].into()).emit(); + FilesOp::Deleting(parent.to_owned(), [UrnBuf::from(real)].into()).emit(); provider::create(&new).await?; } else { provider::create_dir_all(&parent).await.ok(); @@ -56,8 +56,8 @@ impl Create { provider::create(&new).await?; } - if let Ok(f) = File::new(new.clone()).await { - FilesOp::Upserting(parent, [(f.urn_owned(), f)].into()).emit(); + if let Ok(f) = File::new(&new).await { + FilesOp::Upserting(parent.into(), [(f.urn_owned(), f)].into()).emit(); MgrProxy::reveal(&new) } Ok(()) diff --git a/yazi-actor/src/mgr/open.rs b/yazi-actor/src/mgr/open.rs index 8803554e..99fd190c 100644 --- a/yazi-actor/src/mgr/open.rs +++ b/yazi-actor/src/mgr/open.rs @@ -55,7 +55,7 @@ impl Actor for Open { tokio::spawn(async move { let mut files = Vec::with_capacity(todo.len()); for i in todo { - if let Ok(f) = File::new(targets[i].0.clone()).await { + if let Ok(f) = File::new(&targets[i].0).await { files.push(f); } } diff --git a/yazi-actor/src/mgr/rename.rs b/yazi-actor/src/mgr/rename.rs index 285016e0..d5b764eb 100644 --- a/yazi-actor/src/mgr/rename.rs +++ b/yazi-actor/src/mgr/rename.rs @@ -69,15 +69,15 @@ impl Rename { if let Some(o) = overwritten { ok_or_not_found(provider::rename(&p_new.join(&o), &new).await)?; - FilesOp::Deleting(p_new.clone(), [UrnBuf::from(o)].into()).emit(); + FilesOp::Deleting(p_new.to_owned(), [UrnBuf::from(o)].into()).emit(); } - let file = File::new(new.clone()).await?; + let file = File::new(&new).await?; if p_new == p_old { - FilesOp::Upserting(p_old, [(n_old, file)].into()).emit(); + FilesOp::Upserting(p_old.into(), [(n_old, file)].into()).emit(); } else { - FilesOp::Deleting(p_old, [n_old].into()).emit(); - FilesOp::Upserting(p_new, [(n_new, file)].into()).emit(); + FilesOp::Deleting(p_old.into(), [n_old].into()).emit(); + FilesOp::Upserting(p_new.into(), [(n_new, file)].into()).emit(); } MgrProxy::reveal(&new); diff --git a/yazi-actor/src/mgr/reveal.rs b/yazi-actor/src/mgr/reveal.rs index e525f653..635fe207 100644 --- a/yazi-actor/src/mgr/reveal.rs +++ b/yazi-actor/src/mgr/reveal.rs @@ -26,7 +26,7 @@ impl Actor for Reveal { // If the child is not hovered, which means it doesn't exist, // create a dummy file if !opt.no_dummy && tab.hovered().is_none_or(|f| &child != f.urn()) { - let op = FilesOp::Creating(parent, vec![File::from_dummy(opt.target, None)]); + let op = FilesOp::Creating(parent.into(), vec![File::from_dummy(opt.target, None)]); tab.current.update_pub(tab.id, op); } diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index dfa0c04b..47a6a67a 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -32,7 +32,7 @@ impl Boot { }; if provider::metadata(&entry).await.is_ok_and(|m| m.is_file()) { - (parent, child) + (parent.into(), child) } else { (entry, UrnBuf::default()) } diff --git a/yazi-core/src/mgr/linked.rs b/yazi-core/src/mgr/linked.rs index aade9d5a..ea7a5830 100644 --- a/yazi-core/src/mgr/linked.rs +++ b/yazi-core/src/mgr/linked.rs @@ -17,13 +17,15 @@ impl DerefMut for Linked { } impl Linked { - pub fn from_dir<'a, 'b>(&'a self, url: &'b UrlBuf) -> Box + 'b> + pub fn from_dir<'a, 'b, T>(&'a self, url: T) -> Box + 'b> where 'a: 'b, + T: Into>, { + let url = url.into(); if url.scheme.is_virtual() { Box::new(iter::empty()) - } else if let Some(to) = self.get(url) { + } else if let Some(to) = self.get(&url) { Box::new(self.iter().filter(move |(k, v)| *v == to && *k != url).map(|(k, _)| k)) } else { Box::new(self.iter().filter(move |(_, v)| *v == url).map(|(k, _)| k)) @@ -34,7 +36,7 @@ impl Linked { if url.scheme.is_virtual() { vec![] } else if let Some((parent, urn)) = url.pair() { - self.from_dir(&parent).map(|u| u.join(&urn)).collect() + self.from_dir(parent).map(|u| u.join(&urn)).collect() } else { vec![] } diff --git a/yazi-core/src/mgr/watcher.rs b/yazi-core/src/mgr/watcher.rs index 87be9510..55ac0055 100644 --- a/yazi-core/src/mgr/watcher.rs +++ b/yazi-core/src/mgr/watcher.rs @@ -68,9 +68,12 @@ impl Watcher { if !watched.contains(&p) && !LINKED.read().from_dir(&p).any(|u| watched.contains(u)) { continue; } - out_tx.send(u).ok(); - if !parents.contains(&p) { - out_tx.send(p.clone()).ok(); + if parents.contains(&p) { + out_tx.send(u).ok(); + } else { + let p = p.to_owned(); + out_tx.send(u).ok(); + out_tx.send(p.to_owned()).ok(); parents.insert(p); } } @@ -134,8 +137,8 @@ impl Watcher { for u in urls { let Some((parent, urn)) = u.pair() else { continue }; - let Ok(file) = File::new(u).await else { - ops.push(FilesOp::Deleting(parent, [urn].into())); + let Ok(file) = File::new(&u).await else { + ops.push(FilesOp::Deleting(parent.into(), [urn].into())); continue; }; @@ -144,11 +147,11 @@ impl Watcher { || realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s); if !eq { - ops.push(FilesOp::Deleting(parent, [urn].into())); + ops.push(FilesOp::Deleting(parent.into(), [urn].into())); continue; } - ops.push(FilesOp::Upserting(parent, [(urn, file)].into())); + ops.push(FilesOp::Upserting(parent.into(), [(urn, file)].into())); } FilesOp::mutate(ops); diff --git a/yazi-core/src/tab/selected.rs b/yazi-core/src/tab/selected.rs index 229c5502..20898562 100644 --- a/yazi-core/src/tab/selected.rs +++ b/yazi-core/src/tab/selected.rs @@ -72,7 +72,7 @@ impl Selected { self.inner.extend(urls.iter().enumerate().map(|(i, u)| (u.into(), now + i as u64))); for u in parents { - *self.parents.entry(UrlBufCov(u)).or_insert(0) += self.inner.len() - len; + *self.parents.entry_ref(&UrlCov::new(u)).or_default() += self.inner.len() - len; } urls.len() } diff --git a/yazi-fs/src/file.rs b/yazi-fs/src/file.rs index 7005d547..bb339dca 100644 --- a/yazi-fs/src/file.rs +++ b/yazi-fs/src/file.rs @@ -1,7 +1,7 @@ use std::{ffi::OsStr, fs::{FileType, Metadata}, hash::{BuildHasher, Hash, Hasher}, ops::Deref}; use anyhow::Result; -use yazi_shared::url::{Uri, UrlBuf, Urn, UrnBuf}; +use yazi_shared::url::{Uri, UrlBuf, UrlCow, Urn, UrnBuf}; use crate::{cha::Cha, provider}; @@ -21,9 +21,10 @@ impl Deref for File { impl File { #[inline] - pub async fn new(url: UrlBuf) -> Result { + pub async fn new(url: impl Into>) -> Result { + let url = url.into(); let meta = provider::symlink_metadata(&url).await?; - Ok(Self::from_follow(url, meta).await) + Ok(Self::from_follow(url.into_owned(), meta).await) } #[inline] diff --git a/yazi-fs/src/op.rs b/yazi-fs/src/op.rs index 419d7773..b3f1303f 100644 --- a/yazi-fs/src/op.rs +++ b/yazi-fs/src/op.rs @@ -55,9 +55,9 @@ impl FilesOp { let Some(o_p) = o.parent_url() else { continue }; let Some(n_p) = n.url.parent_url() else { continue }; if o_p != n_p { - parents.entry(o_p).or_default().0.insert(o.urn_owned()); + parents.entry_ref(&o_p).or_default().0.insert(o.urn_owned()); } - parents.entry(n_p).or_default().1.insert(n.urn_owned(), n); + parents.entry_ref(&n_p).or_default().1.insert(n.urn_owned(), n); } for (p, (o, n)) in parents { match (o.is_empty(), n.is_empty()) { @@ -124,7 +124,7 @@ impl FilesOp { } else if maybe_exists(cwd).await { Self::IOErr(cwd.clone(), kind).emit(); } else if let Some((p, n)) = cwd.pair() { - Self::Deleting(p, [n].into()).emit(); + Self::Deleting(p.into(), [n].into()).emit(); } } diff --git a/yazi-parser/src/mgr/cd.rs b/yazi-parser/src/mgr/cd.rs index 09a2a015..ba7578a7 100644 --- a/yazi-parser/src/mgr/cd.rs +++ b/yazi-parser/src/mgr/cd.rs @@ -1,6 +1,6 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use yazi_fs::path::expand_url; -use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}}; +use yazi_shared::{event::CmdCow, url::{Url, UrlBuf, UrlCow}}; #[derive(Debug)] pub struct CdOpt { @@ -33,6 +33,10 @@ impl From<(UrlBuf, CdSource)> for CdOpt { } } +impl From<(Url<'_>, CdSource)> for CdOpt { + fn from((target, source): (Url, CdSource)) -> Self { Self::from((target.to_owned(), source)) } +} + impl FromLua for CdOpt { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-scheduler/src/prework/prework.rs b/yazi-scheduler/src/prework/prework.rs index 9bf45afd..2eb27257 100644 --- a/yazi-scheduler/src/prework/prework.rs +++ b/yazi-scheduler/src/prework/prework.rs @@ -92,7 +92,7 @@ impl Prework { let parent = buf[0].0.parent_url().unwrap(); FilesOp::Size( - parent, + parent.into(), HashMap::from_iter(buf.into_iter().map(|(u, s)| (u.urn_owned(), s))), ) .emit(); diff --git a/yazi-shared/src/loc/buf.rs b/yazi-shared/src/loc/buf.rs index 3057f0df..0e0d067f 100644 --- a/yazi-shared/src/loc/buf.rs +++ b/yazi-shared/src/loc/buf.rs @@ -85,16 +85,19 @@ impl LocBuf { } pub fn zeroed(path: impl Into) -> Self { - let mut loc = Self::from(path.into()); - (loc.uri, loc.urn) = (0, 0); - loc + let loc = Self::from(path.into()); + let Loc { inner, uri, urn } = Loc::zeroed(&loc.inner); + + debug_assert!(inner.as_os_str() == loc.inner.as_os_str()); + Self { inner: loc.inner, uri, urn } } pub fn floated(path: impl Into, base: &Path) -> Self { - let mut loc = Self::from(path.into()); - loc.uri = - loc.inner.strip_prefix(base).expect("Loc must start with the given base").as_os_str().len(); - loc + let loc = Self::from(path.into()); + let Loc { inner, uri, urn } = Loc::floated(&loc.inner, base); + + debug_assert!(inner.as_os_str() == loc.inner.as_os_str()); + Self { inner: loc.inner, uri, urn } } #[inline] diff --git a/yazi-shared/src/loc/loc.rs b/yazi-shared/src/loc/loc.rs index 42280a92..3c11ade7 100644 --- a/yazi-shared/src/loc/loc.rs +++ b/yazi-shared/src/loc/loc.rs @@ -110,9 +110,25 @@ impl<'a> Loc<'a> { Ok(loc) } + pub fn zeroed + ?Sized>(path: &'a T) -> Self { + let mut loc = Self::from(path.as_ref()); + (loc.uri, loc.urn) = (0, 0); + loc + } + + pub fn floated + ?Sized>(path: &'a T, base: &Path) -> Self { + let mut loc = Self::from(path.as_ref()); + loc.uri = + loc.inner.strip_prefix(base).expect("Loc must start with the given base").as_os_str().len(); + loc + } + #[inline] pub fn as_loc(self) -> Loc<'a> { self } + #[inline] + pub fn as_path(self) -> &'a Path { self.inner } + #[inline] pub fn uri(self) -> &'a Uri { Uri::new(unsafe { @@ -161,6 +177,9 @@ impl<'a> Loc<'a> { #[inline] pub fn name(self) -> &'a OsStr { self.inner.file_name().unwrap_or(OsStr::new("")) } + #[inline] + pub fn parent(self) -> Option<&'a Path> { self.inner.parent() } + #[inline] pub fn triple(self) -> (&'a Path, &'a Path, &'a Path) { let len = self.bytes().len(); diff --git a/yazi-shared/src/pool/mod.rs b/yazi-shared/src/pool/mod.rs index 8a160e80..dd9924e8 100644 --- a/yazi-shared/src/pool/mod.rs +++ b/yazi-shared/src/pool/mod.rs @@ -1,6 +1,13 @@ yazi_macro::mod_flat!(pool ptr symbol traits); -static SYMBOLS: crate::RoCell>> = - crate::RoCell::new(); +static SYMBOLS: crate::RoCell< + parking_lot::Mutex>, +> = crate::RoCell::new(); pub(super) fn init() { SYMBOLS.with(<_>::default); } + +#[inline] +pub(super) fn compute_hash(value: T) -> u64 { + use core::hash::BuildHasher; + foldhash::fast::FixedState::default().hash_one(value) +} diff --git a/yazi-shared/src/pool/pool.rs b/yazi-shared/src/pool/pool.rs index 5fcd79ec..509c4112 100644 --- a/yazi-shared/src/pool/pool.rs +++ b/yazi-shared/src/pool/pool.rs @@ -1,6 +1,8 @@ use std::marker::PhantomData; -use crate::pool::{SYMBOLS, Symbol, SymbolPtr}; +use hashbrown::hash_map::RawEntryMut; + +use crate::pool::{SYMBOLS, Symbol, SymbolPtr, compute_hash}; pub struct Pool { _phantom: PhantomData, @@ -8,18 +10,23 @@ pub struct Pool { impl Pool<[u8]> { pub fn intern(value: &[u8]) -> Symbol<[u8]> { - let mut lock = SYMBOLS.lock(); + let hash = compute_hash(value); - if let Some((ptr, count)) = lock.get_key_value_mut(value) { - *count += 1; - return Symbol::new(ptr.clone()); + match SYMBOLS.lock().raw_entry_mut().from_key_hashed_nocheck(hash, value) { + RawEntryMut::Occupied(mut oe) => { + let (ptr, count) = oe.get_key_value_mut(); + + *count += 1; + Symbol::new(ptr.clone()) + } + RawEntryMut::Vacant(ve) => { + let boxed = value.to_vec().into_boxed_slice(); + let ptr = SymbolPtr::leaked(Box::leak(boxed)); + + ve.insert_hashed_nocheck(hash, ptr.clone(), 1); + Symbol::new(ptr) + } } - - let boxed = value.to_vec().into_boxed_slice(); - let ptr = SymbolPtr::leaked(Box::leak(boxed)); - - lock.insert(ptr.clone(), 1); - Symbol::new(ptr) } } diff --git a/yazi-shared/src/pool/symbol.rs b/yazi-shared/src/pool/symbol.rs index faf37b48..92fc38d4 100644 --- a/yazi-shared/src/pool/symbol.rs +++ b/yazi-shared/src/pool/symbol.rs @@ -1,6 +1,8 @@ use std::{hash::{Hash, Hasher}, marker::PhantomData, mem::ManuallyDrop, ops::Deref, str}; -use crate::pool::{Pool, SYMBOLS, SymbolPtr}; +use hashbrown::hash_map::RawEntryMut; + +use crate::pool::{Pool, SYMBOLS, SymbolPtr, compute_hash}; pub struct Symbol { ptr: SymbolPtr, @@ -13,22 +15,29 @@ unsafe impl Sync for Symbol {} impl Clone for Symbol { fn clone(&self) -> Self { - *SYMBOLS.lock().get_mut(&self.ptr).unwrap() += 1; + let hash = compute_hash(&self.ptr); + match SYMBOLS.lock().raw_entry_mut().from_key_hashed_nocheck(hash, &self.ptr) { + RawEntryMut::Occupied(mut oe) => *oe.get_mut() += 1, + RawEntryMut::Vacant(_) => unreachable!(), + } Symbol::new(self.ptr.clone()) } } impl Drop for Symbol { fn drop(&mut self) { - let mut lock = SYMBOLS.lock(); - let count = lock.get_mut(&self.ptr).unwrap(); + let hash = compute_hash(&self.ptr); + match SYMBOLS.lock().raw_entry_mut().from_key_hashed_nocheck(hash, &self.ptr) { + RawEntryMut::Occupied(mut oe) => { + let count = oe.get_mut(); + *count -= 1; - *count -= 1; - if *count == 0 { - lock.remove(&self.ptr); - unsafe { - drop(Box::from_raw(self.ptr.as_ptr())); + if *count == 0 { + oe.remove(); + drop(unsafe { Box::from_raw(self.ptr.as_ptr()) }); + } } + RawEntryMut::Vacant(_) => unreachable!(), } } } @@ -38,7 +47,7 @@ impl AsRef<[u8]> for Symbol<[u8]> { } impl AsRef for Symbol { - fn as_ref(&self) -> &str { unsafe { str::from_utf8_unchecked(self.ptr.as_ref()) } } + fn as_ref(&self) -> &str { unsafe { str::from_utf8_unchecked(self.ptr.bytes()) } } } impl Deref for Symbol<[u8]> { @@ -74,19 +83,6 @@ impl Hash for Symbol { fn hash(&self, state: &mut H) { self.ptr.as_ptr().hash(state); } } -// --- PartialOrd -impl PartialOrd for Symbol<[u8]> { - fn partial_cmp(&self, other: &Self) -> Option { - self.as_ref().partial_cmp(other.as_ref()) - } -} - -impl PartialOrd for Symbol { - fn partial_cmp(&self, other: &Self) -> Option { - self.as_ref().partial_cmp(other.as_ref()) - } -} - // --- Ord impl Ord for Symbol<[u8]> { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_ref().cmp(other.as_ref()) } @@ -96,6 +92,15 @@ impl Ord for Symbol { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_ref().cmp(other.as_ref()) } } +// --- PartialOrd +impl PartialOrd for Symbol<[u8]> { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +impl PartialOrd for Symbol { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + // --- Debug impl std::fmt::Debug for Symbol<[u8]> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/yazi-shared/src/url/buf.rs b/yazi-shared/src/url/buf.rs index 13cf9727..97f14d0b 100644 --- a/yazi-shared/src/url/buf.rs +++ b/yazi-shared/src/url/buf.rs @@ -31,7 +31,7 @@ impl From<&Url<'_>> for UrlBuf { } impl From> for UrlBuf { - fn from(value: Url<'_>) -> Self { Self::from(&value) } + fn from(url: Url<'_>) -> Self { Self { loc: url.loc.into(), scheme: url.scheme } } } impl From<&UrlBuf> for UrlBuf { @@ -91,35 +91,23 @@ impl PartialEq> for &UrlBuf { } impl UrlBuf { - pub fn join(&self, path: impl AsRef) -> Self { - use Scheme as S; - - let join = self.loc.join(path); - - let loc = match self.scheme { - S::Regular => join.into(), - S::Search(_) => LocBuf::new(join, self.loc.base(), self.loc.base()), - S::Archive(_) => LocBuf::floated(join, self.loc.base()), - S::Sftp(_) => join.into(), - }; - - Self { loc, scheme: self.scheme.clone() } - } + #[inline] + pub fn join(&self, path: impl AsRef) -> Self { self.as_url().join(path) } #[inline] pub fn components(&self) -> Components<'_> { Components::from(self) } #[inline] - pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other.as_url()) } + pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() } #[inline] pub fn display(&self) -> Display<'_> { Display::new(self) } #[inline] - pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() } + pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other.as_url()) } #[inline] - pub fn parent_url(&self) -> Option { self.as_url().parent_url() } + pub fn parent_url(&self) -> Option> { self.as_url().parent_url() } pub fn strip_prefix(&self, base: impl AsRef) -> Option<&Urn> { use Scheme as S; @@ -172,8 +160,11 @@ impl UrlBuf { Self { loc: self.loc.rebase(base), scheme: self.scheme.clone() } } + // TODO: use Urn instead of UrlBuf #[inline] - pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) } + pub fn pair(&self) -> Option<(Url<'_>, UrnBuf)> { + Some((self.parent_url()?, self.loc.urn_owned())) + } #[inline] pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) } @@ -193,9 +184,7 @@ impl UrlBuf { pub fn is_regular(&self) -> bool { self.as_url().is_regular() } #[inline] - pub fn to_regular(&self) -> Self { - Self { loc: self.loc.to_path().into(), scheme: Scheme::Regular } - } + pub fn to_regular(&self) -> Self { self.as_url().into_regular().into() } #[inline] pub fn into_regular(mut self) -> Self { @@ -243,9 +232,7 @@ impl UrlBuf { } impl Debug for UrlBuf { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}{}", Encode::from(self), self.loc.display()) - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.as_url().fmt(f) } } impl Serialize for UrlBuf { diff --git a/yazi-shared/src/url/cov.rs b/yazi-shared/src/url/cov.rs index 9e6db020..485c4034 100644 --- a/yazi-shared/src/url/cov.rs +++ b/yazi-shared/src/url/cov.rs @@ -62,6 +62,10 @@ impl From> for UrlBufCov { fn from(value: UrlCow<'_>) -> Self { Self(value.into_owned()) } } +impl From> for UrlBufCov { + fn from(value: Url<'_>) -> Self { Self(value.to_owned()) } +} + impl From<&UrlCov<'_>> for UrlBufCov { fn from(value: &UrlCov<'_>) -> Self { Self(UrlBuf::from(&value.0)) } } @@ -87,5 +91,5 @@ impl UrlBufCov { pub fn as_url(&self) -> UrlCov<'_> { UrlCov::from(self) } #[inline] - pub fn parent_url(&self) -> Option { self.0.parent_url().map(UrlBufCov) } + pub fn parent_url(&self) -> Option { self.0.parent_url().map(Into::into) } } diff --git a/yazi-shared/src/url/cow.rs b/yazi-shared/src/url/cow.rs index cc92902a..0ad98abc 100644 --- a/yazi-shared/src/url/cow.rs +++ b/yazi-shared/src/url/cow.rs @@ -118,10 +118,10 @@ impl UrlCow<'_> { pub fn into_owned(self) -> UrlBuf { self.as_url().into() } #[inline] - pub fn parent_url(&self) -> Option { self.as_url().parent_url() } + pub fn parent_url(&self) -> Option> { self.as_url().parent_url() } #[inline] - pub fn pair(&self) -> Option<(UrlBuf, UrnBuf)> { self.as_url().pair() } + pub fn pair(&self) -> Option<(Url<'_>, UrnBuf)> { self.as_url().pair() } pub fn parse(bytes: &[u8]) -> Result<(Scheme, Cow<'_, Path>, Option<(usize, usize)>)> { let mut skip = 0; diff --git a/yazi-shared/src/url/url.rs b/yazi-shared/src/url/url.rs index d345aa79..d89dd2d3 100644 --- a/yazi-shared/src/url/url.rs +++ b/yazi-shared/src/url/url.rs @@ -1,10 +1,10 @@ -use std::{ops::Deref, path::Path}; +use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, ops::Deref, path::Path}; use hashbrown::Equivalent; -use crate::{loc::{Loc, LocBuf}, url::{Components, Scheme, UrlBuf, UrnBuf}}; +use crate::{loc::{Loc, LocBuf}, url::{Components, Encode, Scheme, UrlBuf, UrnBuf}}; -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Eq, Hash, PartialEq)] pub struct Url<'a> { pub loc: Loc<'a>, pub scheme: Scheme, @@ -41,6 +41,13 @@ impl Equivalent for Url<'_> { fn equivalent(&self, key: &UrlBuf) -> bool { self == key } } +// --- Debug +impl Debug for Url<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}{}", Encode::from(self), self.loc.display()) + } +} + impl<'a> Url<'a> { #[inline] pub fn regular + ?Sized>(path: &'a T) -> Self { @@ -50,12 +57,35 @@ impl<'a> Url<'a> { #[inline] pub fn is_regular(&self) -> bool { self.scheme == Scheme::Regular } + #[inline] + pub fn into_regular(self) -> Self { + Self { loc: self.loc.as_path().into(), scheme: Scheme::Regular } + } + + #[inline] + pub fn is_search(&self) -> bool { matches!(self.scheme, Scheme::Search(_)) } + #[inline] pub fn as_url(&'a self) -> Url<'a> { Self::from(self) } #[inline] pub fn to_owned(&self) -> UrlBuf { self.into() } + pub fn join(&self, path: impl AsRef) -> UrlBuf { + use Scheme as S; + + let join = self.loc.join(path); + + let loc = match self.scheme { + S::Regular => join.into(), + S::Search(_) => LocBuf::new(join, self.loc.base(), self.loc.base()), + S::Archive(_) => LocBuf::floated(join, self.loc.base()), + S::Sftp(_) => join.into(), + }; + + UrlBuf { loc, scheme: self.scheme.clone() } + } + pub fn base(&self) -> Option { use Scheme as S; @@ -72,8 +102,7 @@ impl<'a> Url<'a> { }) } - // TODO: use Url instead as the return type - pub fn parent_url(&self) -> Option { + pub fn parent_url(&self) -> Option { use Scheme as S; let parent = self.loc.parent()?; @@ -81,41 +110,43 @@ impl<'a> Url<'a> { Some(match self.scheme { // Regular - S::Regular => UrlBuf { loc: parent.into(), scheme: S::Regular }, + S::Regular => Self { loc: parent.into(), scheme: S::Regular }, // Search - S::Search(_) if uri.is_empty() => UrlBuf { loc: parent.into(), scheme: S::Regular }, - S::Search(_) => UrlBuf { - loc: LocBuf::new(parent, self.loc.base(), self.loc.base()), + S::Search(_) if uri.is_empty() => Self { loc: parent.into(), scheme: S::Regular }, + S::Search(_) => Self { + loc: Loc::new(parent, self.loc.base(), self.loc.base()), scheme: self.scheme.clone(), }, // Archive - S::Archive(_) if uri.is_empty() => UrlBuf { loc: parent.into(), scheme: S::Regular }, + S::Archive(_) if uri.is_empty() => Self { loc: parent.into(), scheme: S::Regular }, S::Archive(_) if uri.nth(1).is_none() => { - UrlBuf { loc: LocBuf::zeroed(parent), scheme: self.scheme.clone() } + Self { loc: Loc::zeroed(parent), scheme: self.scheme.clone() } } S::Archive(_) => { - UrlBuf { loc: LocBuf::floated(parent, self.loc.base()), scheme: self.scheme.clone() } + Self { loc: Loc::floated(parent, self.loc.base()), scheme: self.scheme.clone() } } // SFTP - S::Sftp(_) => UrlBuf { loc: parent.into(), scheme: self.scheme.clone() }, + S::Sftp(_) => Self { loc: parent.into(), scheme: self.scheme.clone() }, }) } #[inline] pub fn components(&self) -> Components<'_> { Components::from(self) } + #[inline] + pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() } + #[inline] pub fn covariant(&self, other: impl Into) -> bool { let other = other.into(); self.scheme.covariant(&other.scheme) && self.loc == other.loc } - // TODO: use Urn instead as the return type #[inline] - pub fn pair(&self) -> Option<(UrlBuf, UrnBuf)> { + pub fn pair(&self) -> Option<(Url<'a>, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) }