refactor: Url::pair() without allocation (#3116)

This commit is contained in:
三咲雅 misaki masa 2025-08-27 23:52:09 +08:00 committed by GitHub
parent c2a94daad6
commit dd7afaa64a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 74 additions and 71 deletions

View file

@ -52,7 +52,7 @@ impl Actor for Cd {
tab.history.insert(rep.url.to_owned(), rep);
// Parent
if let Some(parent) = opt.target.parent_url() {
if let Some(parent) = opt.target.parent() {
tab.parent = Some(tab.history.remove_or(&parent));
}
@ -83,7 +83,7 @@ impl Cd {
return MgrProxy::cd(&url);
}
if let Some(p) = url.parent_url() {
if let Some(p) = url.parent() {
FilesOp::Upserting(p.into(), [(url.urn().to_owned(), file)].into()).emit();
}
MgrProxy::reveal(&url);

View file

@ -33,7 +33,7 @@ impl Actor for Copy {
s.push(opt.separator.transform(&u.os_str()));
}
"dirname" => {
if let Some(p) = u.parent_url() {
if let Some(p) = u.parent() {
s.push(opt.separator.transform(&p.os_str()));
}
}

View file

@ -50,9 +50,9 @@ impl Create {
&& let Some((parent, urn)) = real.pair()
{
ok_or_not_found(provider::remove_file(&new).await)?;
FilesOp::Deleting(parent.into(), [urn].into()).emit();
FilesOp::Deleting(parent.into(), [urn.into()].into()).emit();
provider::create(&new).await?;
} else if let Some(parent) = new.parent_url() {
} else if let Some(parent) = new.parent() {
provider::create_dir_all(&parent).await.ok();
ok_or_not_found(provider::remove_file(&new).await)?;
provider::create(&new).await?;
@ -64,7 +64,7 @@ impl Create {
&& let Some((parent, urn)) = real.pair()
{
let file = File::new(&real).await?;
FilesOp::Upserting(parent.into(), [(urn, file)].into()).emit();
FilesOp::Upserting(parent.into(), [(urn.into(), file)].into()).emit();
MgrProxy::reveal(&real);
}

View file

@ -17,7 +17,7 @@ impl Actor for Follow {
let Some(file) = cx.hovered() else { succ!() };
let Some(link_to) = &file.link_to else { succ!() };
if let Some(p) = file.url.parent_url() {
if let Some(p) = file.url.parent() {
act!(mgr:reveal, cx, clean_url(p.join(link_to)))
} else {
succ!()

View file

@ -15,9 +15,9 @@ impl Actor for Leave {
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
let url = cx
.hovered()
.and_then(|h| h.url.parent_url())
.and_then(|h| h.url.parent())
.filter(|u| u != cx.cwd())
.or_else(|| cx.cwd().parent_url());
.or_else(|| cx.cwd().parent());
let Some(mut url) = url else { succ!() };
if url.is_search() {

View file

@ -111,7 +111,7 @@ impl Actor for OpenDo {
impl Open {
fn guess_folder(cx: &Ctx, url: &UrlBuf) -> bool {
let Some(p) = url.parent_url() else {
let Some(p) = url.parent() else {
return true;
};

View file

@ -48,7 +48,7 @@ impl Actor for Rename {
return;
}
let new = old.parent_url().unwrap().join(name);
let new = old.parent().unwrap().join(name);
if opt.force || !maybe_exists(&new).await || provider::must_identical(&old, &new).await {
Self::r#do(tab, old, new).await.ok();
} else if ConfirmProxy::show(ConfirmCfg::overwrite(&new)).await {
@ -72,7 +72,7 @@ impl Rename {
&& let Some((parent, urn)) = u.pair()
{
ok_or_not_found(provider::rename(&u, &new).await)?;
FilesOp::Deleting(parent.to_owned(), [urn].into()).emit();
FilesOp::Deleting(parent.to_owned(), [urn.into()].into()).emit();
}
let new = provider::casefold(&new).await?;
@ -80,10 +80,10 @@ impl Rename {
let file = File::new(&new).await?;
if new_p == old_p {
FilesOp::Upserting(old_p.into(), [(old_n, file)].into()).emit();
FilesOp::Upserting(old_p.into(), [(old_n.into(), file)].into()).emit();
} else {
FilesOp::Deleting(old_p.into(), [old_n].into()).emit();
FilesOp::Upserting(new_p.into(), [(new_n, file)].into()).emit();
FilesOp::Deleting(old_p.into(), [old_n.into()].into()).emit();
FilesOp::Upserting(new_p.into(), [(new_n.into(), file)].into()).emit();
}
MgrProxy::reveal(&new);

View file

@ -21,17 +21,17 @@ impl Actor for Reveal {
// Try to hover on the child file
let tab = cx.tab_mut();
render!(tab.current.hover(child.as_urn()));
render!(tab.current.hover(child));
// 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.into(), vec![File::from_dummy(opt.target, None)]);
if !opt.no_dummy && tab.hovered().is_none_or(|f| child != f.urn()) {
let op = FilesOp::Creating(parent.into(), vec![File::from_dummy(&opt.target, None)]);
tab.current.update_pub(tab.id, op);
}
// Now, we can safely hover on the target
act!(mgr:hover, cx, Some(child))?;
act!(mgr:hover, cx, Some(child.into()))?;
act!(mgr:peek, cx)?;
act!(mgr:watch, cx)?;

View file

@ -101,7 +101,7 @@ impl UpdateFiles {
fn update_history(cx: &mut Ctx, op: FilesOp) -> Result<Data> {
let tab = &mut cx.tab_mut();
let leave = tab.parent.as_ref().and_then(|f| f.url.parent_url().map(|p| (p, f.url.urn()))).is_some_and(
let leave = tab.parent.as_ref().and_then(|f| f.url.parent().map(|p| (p, f.url.urn()))).is_some_and(
|(p, n)| matches!(op, FilesOp::Deleting(ref parent, ref urns) if *parent == p && urns.contains(n)),
);

View file

@ -102,7 +102,7 @@ impl UserData for Url {
cached_field!(fields, ext, |lua, me| {
me.ext().map(|s| lua.create_string(s.as_encoded_bytes())).transpose()
});
cached_field!(fields, parent, |_, me| Ok(me.parent_url().map(Self::new)));
cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new)));
cached_field!(fields, urn, |_, me| Ok(Urn::new(me.urn())));
cached_field!(fields, base, |_, me| Ok(me.base().map(Self::new)));
cached_field!(fields, domain, |lua, me| {

View file

@ -32,7 +32,7 @@ impl Boot {
};
if provider::metadata(&entry).await.is_ok_and(|m| m.is_file()) {
(parent.into(), child)
(parent.into(), child.into())
} else {
(entry, UrnBuf::default())
}

View file

@ -34,10 +34,10 @@ pub async fn copy_and_seal(from: &Path, to: &Path) -> io::Result<()> {
pub async fn remove_sealed(p: &Path) -> io::Result<()> {
#[cfg(windows)]
{
let mut perm = tokio::fs::metadata(p).await?.permissions();
let mut perm = Local::metadata(p).await?.permissions();
perm.set_readonly(false);
tokio::fs::set_permissions(p, perm).await?;
}
tokio::fs::remove_file(p).await
Local::remove_file(p).await
}

View file

@ -51,7 +51,7 @@ impl Yanked {
let mut it = u.components();
it.next_back().is_some()
&& it.covariant(&dir.components())
&& u.parent_url().is_some_and(|p| p == *dir)
&& u.parent().is_some_and(|p| p == *dir)
})
}

View file

@ -35,10 +35,9 @@ impl Selected {
T: Into<Url<'a>>,
{
let mut grouped: HashMap<_, Vec<_>> = Default::default();
for url in urls {
let u = url.into();
if let Some(p) = u.parent_url() {
grouped.entry(p).or_default().push(u);
for url in urls.into_iter().map(Into::into) {
if let Some(p) = url.parent() {
grouped.entry(p).or_default().push(url);
}
}
grouped.into_values().map(|v| self.add_same(v)).sum()
@ -57,14 +56,14 @@ impl Selected {
}
// If it has appeared as a child
let mut parent = urls[0].parent_url();
let mut parent = urls[0].parent();
let mut parents = vec![];
while let Some(u) = parent {
if self.inner.contains_key(&UrlCov::new(&u)) {
return 0;
}
parent = u.parent_url();
parent = u.parent();
parents.push(u);
}
@ -88,10 +87,9 @@ impl Selected {
T: Into<Url<'a>>,
{
let mut grouped: HashMap<_, Vec<_>> = Default::default();
for url in urls {
let u = url.into();
if let Some(p) = u.parent_url() {
grouped.entry(p).or_default().push(u);
for url in urls.into_iter().map(Into::into) {
if let Some(p) = url.parent() {
grouped.entry(p).or_default().push(url);
}
}
@ -116,8 +114,8 @@ impl Selected {
return 0;
}
// FIXME: use UrlCov::parent_url() instead
let mut parent = first.parent_url();
// FIXME: use UrlCov::parent() instead
let mut parent = first.parent();
while let Some(u) = parent {
let n = self.parents.get_mut(&UrlCov::new(&u)).unwrap();
@ -126,7 +124,7 @@ impl Selected {
self.parents.remove(&UrlCov::new(&u));
}
parent = u.parent_url();
parent = u.parent();
}
count
}

View file

@ -1,5 +1,3 @@
// FIXME: VFS
use anyhow::Result;
use tokio::{io, select, sync::{mpsc, oneshot}, time};
use yazi_shared::url::{Component, Url, UrlBuf};
@ -159,7 +157,7 @@ pub fn max_common_root(urls: &[UrlBuf]) -> usize {
return urls[0].components().count() - 1;
}
let mut it = urls.iter().map(|u| u.parent_url());
let mut it = urls.iter().map(|u| u.parent());
let Some(first) = it.next().unwrap() else {
return 0; // The first URL has no parent
};

View file

@ -54,8 +54,8 @@ impl FilesOp {
pub fn rename(map: HashMap<UrlBuf, File>) {
let mut parents: HashMap<_, (HashSet<_>, HashMap<_, _>)> = Default::default();
for (o, n) in map {
let Some(o_p) = o.parent_url() else { continue };
let Some(n_p) = n.url.parent_url() else { continue };
let Some(o_p) = o.parent() else { continue };
let Some(n_p) = n.url.parent() else { continue };
if o_p != n_p {
parents.entry_ref(&o_p).or_default().0.insert(o.urn().to_owned());
}
@ -126,7 +126,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.into(), [n].into()).emit();
Self::Deleting(p.into(), [n.into()].into()).emit();
}
}

View file

@ -11,7 +11,7 @@ impl Utils {
pub(super) fn file_cache(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, t: Table| {
let file: FileRef = t.raw_get("file")?;
if file.url.parent_url() == Some(yazi_shared::url::Url::regular(&YAZI.preview.cache_dir)) {
if file.url.parent() == Some(yazi_shared::url::Url::regular(&YAZI.preview.cache_dir)) {
return Ok(None);
}

View file

@ -77,7 +77,7 @@ impl File {
};
let src = if task.relative {
path_relative_to(provider::canonicalize(&task.to.parent_url().unwrap()).await?.loc, &src)?
path_relative_to(provider::canonicalize(&task.to.parent().unwrap()).await?.loc, &src)?
} else {
src
};

View file

@ -90,7 +90,7 @@ impl Prework {
}
}
let parent = buf[0].0.parent_url().unwrap();
let parent = buf[0].0.parent().unwrap();
FilesOp::Size(
parent.into(),
HashMap::from_iter(buf.into_iter().map(|(u, s)| (u.urn().to_owned(), s))),

View file

@ -3,7 +3,6 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, hash::BuildHasher, p
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::UrnBuf;
use crate::{loc::LocBuf, pool::Pool, url::{Components, Display, Encode, EncodeTilded, Scheme, Uri, Url, UrlCow, Urn}};
#[derive(Clone, Default, Eq, Ord, PartialOrd, PartialEq, Hash)]
@ -96,7 +95,7 @@ impl UrlBuf {
pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other) }
#[inline]
pub fn parent_url(&self) -> Option<Url<'_>> { self.as_url().parent_url() }
pub fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
#[inline]
pub fn starts_with<'a>(&self, base: impl Into<Url<'a>>) -> bool {
@ -156,7 +155,7 @@ impl UrlBuf {
}
#[inline]
pub fn pair(&self) -> Option<(Url<'_>, UrnBuf)> { self.as_url().pair() }
pub fn pair(&self) -> Option<(Url<'_>, &Urn)> { self.as_url().pair() }
#[inline]
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
@ -312,7 +311,7 @@ mod tests {
}
#[test]
fn test_parent_url() -> anyhow::Result<()> {
fn test_parent() -> anyhow::Result<()> {
crate::init_tests();
let cases = [
// Regular
@ -339,7 +338,7 @@ mod tests {
for (path, expected) in cases {
let path: UrlBuf = path.parse()?;
assert_eq!(path.parent_url().map(|u| format!("{:?}", u)).as_deref(), expected);
assert_eq!(path.parent().map(|u| format!("{:?}", u)).as_deref(), expected);
}
Ok(())
@ -355,7 +354,7 @@ mod tests {
let u = u.into_search("kw");
assert_eq!(format!("{u:?}"), "search://kw//root");
assert_eq!(format!("{:?}", u.parent_url().unwrap()), "/");
assert_eq!(format!("{:?}", u.parent().unwrap()), "/");
let u = u.join("examples");
assert_eq!(format!("{u:?}"), format!("search://kw:1:1//root{S}examples"));
@ -363,13 +362,13 @@ mod tests {
let u = u.join("README.md");
assert_eq!(format!("{u:?}"), format!("search://kw:2:2//root{S}examples{S}README.md"));
let u = u.parent_url().unwrap();
let u = u.parent().unwrap();
assert_eq!(format!("{u:?}"), format!("search://kw:1:1//root{S}examples"));
let u = u.parent_url().unwrap();
let u = u.parent().unwrap();
assert_eq!(format!("{u:?}"), "search://kw//root");
let u = u.parent_url().unwrap();
let u = u.parent().unwrap();
assert_eq!(format!("{u:?}"), "/");
Ok(())

View file

@ -95,5 +95,5 @@ impl UrlBufCov {
pub fn as_url(&self) -> UrlCov<'_> { UrlCov::from(self) }
#[inline]
pub fn parent_url(&self) -> Option<UrlBufCov> { self.0.parent_url().map(Into::into) }
pub fn parent(&self) -> Option<UrlBufCov> { self.0.parent().map(Into::into) }
}

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, path::{Path, PathBuf}};
use anyhow::Result;
use percent_encoding::percent_decode;
use crate::{IntoOsStr, loc::{Loc, LocBuf}, url::{Components, Scheme, Url, UrlBuf, UrnBuf}};
use crate::{IntoOsStr, loc::{Loc, LocBuf}, url::{Components, Scheme, Url, UrlBuf, Urn}};
#[derive(Debug)]
pub enum UrlCow<'a> {
@ -35,6 +35,10 @@ impl From<UrlCow<'_>> for UrlBuf {
fn from(value: UrlCow<'_>) -> Self { value.into_owned() }
}
impl From<&UrlCow<'_>> for UrlBuf {
fn from(value: &UrlCow<'_>) -> Self { value.as_url().into() }
}
impl<'a> TryFrom<&'a [u8]> for UrlCow<'a> {
type Error = anyhow::Error;
@ -107,6 +111,7 @@ impl UrlCow<'_> {
}
}
#[inline]
pub fn as_url(&self) -> Url<'_> {
match self {
UrlCow::Borrowed(u) => u.as_url(),
@ -115,13 +120,18 @@ impl UrlCow<'_> {
}
#[inline]
pub fn into_owned(self) -> UrlBuf { self.as_url().into() }
pub fn into_owned(self) -> UrlBuf {
match self {
UrlCow::Borrowed(u) => u.into(),
UrlCow::Owned(u) => u,
}
}
#[inline]
pub fn parent_url(&self) -> Option<Url<'_>> { self.as_url().parent_url() }
pub fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
#[inline]
pub fn pair(&self) -> Option<(Url<'_>, UrnBuf)> { self.as_url().pair() }
pub fn pair(&self) -> Option<(Url<'_>, &Urn)> { self.as_url().pair() }
pub fn parse(bytes: &[u8]) -> Result<(Scheme, Cow<'_, Path>, Option<(usize, usize)>)> {
let mut skip = 0;

View file

@ -162,7 +162,7 @@ mod tests {
use super::*;
#[test]
fn test_decode_port() -> Result<()> {
fn test_decode_ports() -> Result<()> {
fn assert(s: &str, len: usize, uri: Option<usize>, urn: Option<usize>) -> Result<()> {
let mut n = usize::MAX;
let port = Scheme::decode_ports(s.as_bytes(), &mut n)?;

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, path::Path};
use hashbrown::Equivalent;
use crate::{loc::{Loc, LocBuf}, url::{Components, Encode, Scheme, Uri, UrlBuf, Urn, UrnBuf}};
use crate::{loc::{Loc, LocBuf}, url::{Components, Encode, Scheme, Uri, UrlBuf, Urn}};
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Url<'a> {
@ -121,7 +121,7 @@ impl<'a> Url<'a> {
})
}
pub fn parent_url(&self) -> Option<Self> {
pub fn parent(&self) -> Option<Self> {
use Scheme as S;
let parent = self.loc.parent()?;
@ -177,9 +177,7 @@ impl<'a> Url<'a> {
}
#[inline]
pub fn pair(&self) -> Option<(Url<'a>, UrnBuf)> {
Some((self.parent_url()?, self.loc.urn().to_owned()))
}
pub fn pair(&self) -> Option<(Url<'a>, &'a Urn)> { Some((self.parent()?, self.loc.urn())) }
#[inline]
pub fn as_path(&self) -> Option<&'a Path> {

View file

@ -51,7 +51,7 @@ impl Backend {
{
let (mut todo, watched) = (HashSet::new(), WATCHED.read());
for url in urls.into_iter().map(Into::into) {
let Some(parent) = url.parent_url() else { continue };
let Some(parent) = url.parent() else { continue };
if todo.contains(&parent) {
todo.insert(url);
} else if watched.contains(&parent)

View file

@ -61,18 +61,18 @@ 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.into(), [urn].into()));
ops.push(FilesOp::Deleting(parent.into(), [urn.into()].into()));
continue;
};
if let Some(p) = file.url.as_path()
&& !local::must_case_match(p).await
{
ops.push(FilesOp::Deleting(parent.into(), [urn].into()));
ops.push(FilesOp::Deleting(parent.into(), [urn.into()].into()));
continue;
}
ops.push(FilesOp::Upserting(parent.into(), [(urn, file)].into()));
ops.push(FilesOp::Upserting(parent.into(), [(urn.into(), file)].into()));
}
FilesOp::mutate(ops);