feat: dynamic paths (#3316)

This commit is contained in:
三咲雅 misaki masa 2025-11-06 18:01:02 +08:00 committed by GitHub
parent cc8f9a7177
commit 4b56e73235
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 389 additions and 222 deletions

View file

@ -77,7 +77,7 @@ impl UserData for File {
yazi_binding::impl_file_methods!(methods);
methods.add_method("size", |_, me, ()| {
Ok(if me.is_dir() { me.folder.files.sizes.get(me.urn()).copied() } else { Some(me.len) })
Ok(if me.is_dir() { me.folder.files.sizes.get(&me.urn()).copied() } else { Some(me.len) })
});
methods.add_method("mime", |lua, me, ()| {
lua.named_registry_value::<AnyUserData>("cx")?.borrow_scoped(|core: &yazi_core::Core| {

View file

@ -9,7 +9,7 @@ use yazi_fs::{File, FilesOp, path::expand_url};
use yazi_macro::{act, err, render, succ};
use yazi_parser::mgr::CdOpt;
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
use yazi_shared::{Debounce, data::Data, errors::InputError, url::{AsUrl, UrlBuf, UrlLike}};
use yazi_shared::{Debounce, data::Data, errors::InputError, path::PathLike, url::{AsUrl, UrlBuf, UrlLike}};
use yazi_vfs::VfsFile;
use crate::{Actor, Ctx};
@ -85,7 +85,7 @@ impl Cd {
}
if let Some(p) = url.parent() {
FilesOp::Upserting(p.into(), [(url.urn().to_owned(), file)].into()).emit();
FilesOp::Upserting(p.into(), [(url.urn().owned(), file)].into()).emit();
}
MgrProxy::reveal(&url);
}

View file

@ -4,7 +4,7 @@ use yazi_fs::{File, FilesOp};
use yazi_macro::{ok_or_not_found, succ};
use yazi_parser::mgr::CreateOpt;
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
use yazi_shared::{data::Data, path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@ -51,7 +51,7 @@ impl Create {
&& let Some((parent, urn)) = real.pair()
{
ok_or_not_found!(provider::remove_file(&new).await);
FilesOp::Deleting(parent.into(), [urn.to_owned()].into()).emit();
FilesOp::Deleting(parent.into(), [urn.owned()].into()).emit();
provider::create(&new).await?;
} else if let Some(parent) = new.parent() {
provider::create_dir_all(parent).await.ok();
@ -65,7 +65,7 @@ impl Create {
&& let Some((parent, urn)) = real.pair()
{
let file = File::new(&real).await?;
FilesOp::Upserting(parent.into(), [(urn.to_owned(), file)].into()).emit();
FilesOp::Upserting(parent.into(), [(urn.owned(), file)].into()).emit();
MgrProxy::reveal(&real);
}

View file

@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_fs::Filter;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::FilterOpt;
use yazi_shared::data::Data;
use yazi_shared::{data::Data, path::PathLike};
use crate::{Actor, Ctx};
@ -16,10 +16,10 @@ impl Actor for FilterDo {
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let filter = if opt.query.is_empty() { None } else { Some(Filter::new(&opt.query, opt.case)?) };
let hovered = cx.hovered().map(|f| f.urn().to_owned());
let hovered = cx.hovered().map(|f| f.urn().owned());
cx.current_mut().files.set_filter(filter);
if cx.hovered().map(|f| f.urn()) != hovered.as_deref() {
if cx.hovered().map(|f| f.urn()) != hovered.as_ref().map(Into::into) {
act!(mgr:hover, cx, hovered)?;
act!(mgr:peek, cx)?;
act!(mgr:watch, cx)?;

View file

@ -3,7 +3,7 @@ use yazi_core::tab::Folder;
use yazi_fs::FolderStage;
use yazi_macro::{act, render, render_and, succ};
use yazi_parser::mgr::HiddenOpt;
use yazi_shared::data::Data;
use yazi_shared::{data::Data, path::PathLike};
use crate::{Actor, Ctx};
@ -18,7 +18,7 @@ impl Actor for Hidden {
let state = opt.state.bool(cx.tab().pref.show_hidden);
cx.tab_mut().pref.show_hidden = state;
let hovered = cx.hovered().map(|f| f.urn().to_owned());
let hovered = cx.hovered().map(|f| f.urn().owned());
let apply = |f: &mut Folder| {
if f.stage == FolderStage::Loading {
render!();
@ -43,7 +43,7 @@ impl Actor for Hidden {
{
render!(h.repos(None));
act!(mgr:peek, cx, true)?;
} else if hovered.as_deref() != cx.hovered().map(|f| f.urn()) {
} else if cx.hovered().map(|f| f.urn()) != hovered.as_ref().map(Into::into) {
act!(mgr:peek, cx)?;
act!(mgr:watch, cx)?;
}

View file

@ -22,7 +22,7 @@ impl Actor for Hover {
}
// Repos CWD
tab.current.repos(opt.urn.as_deref());
tab.current.repos(opt.urn.as_ref().map(Into::into));
// Turn on tracing
if let (Some(h), Some(u)) = (tab.hovered(), opt.urn)

View file

@ -5,7 +5,7 @@ use yazi_fs::{File, FilesOp};
use yazi_macro::{act, err, ok_or_not_found, succ};
use yazi_parser::mgr::RenameOpt;
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
use yazi_shared::{Id, data::Data, url::{UrlBuf, UrlLike}};
use yazi_shared::{Id, data::Data, path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@ -73,7 +73,7 @@ impl Rename {
&& let Some((parent, urn)) = u.pair()
{
ok_or_not_found!(provider::rename(&u, &new).await);
FilesOp::Deleting(parent.to_owned(), [urn.to_owned()].into()).emit();
FilesOp::Deleting(parent.to_owned(), [urn.owned()].into()).emit();
}
let new = provider::casefold(&new).await?;
@ -81,10 +81,10 @@ impl Rename {
let file = File::new(&new).await?;
if new_p == old_p {
FilesOp::Upserting(old_p.into(), [(old_n.to_owned(), file)].into()).emit();
FilesOp::Upserting(old_p.into(), [(old_n.owned(), file)].into()).emit();
} else {
FilesOp::Deleting(old_p.into(), [old_n.to_owned()].into()).emit();
FilesOp::Upserting(new_p.into(), [(new_n.to_owned(), file)].into()).emit();
FilesOp::Deleting(old_p.into(), [old_n.owned()].into()).emit();
FilesOp::Upserting(new_p.into(), [(new_n.owned(), file)].into()).emit();
}
MgrProxy::reveal(&new);

View file

@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_fs::{File, FilesOp};
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::RevealOpt;
use yazi_shared::{data::Data, url::UrlLike};
use yazi_shared::{data::Data, path::PathLike, url::UrlLike};
use crate::{Actor, Ctx};
@ -31,7 +31,7 @@ impl Actor for Reveal {
}
// Now, we can safely hover on the target
act!(mgr:hover, cx, Some(child.to_owned()))?;
act!(mgr:hover, cx, Some(child.owned()))?;
act!(mgr:peek, cx)?;
act!(mgr:watch, cx)?;

View file

@ -3,7 +3,7 @@ use yazi_core::tab::Folder;
use yazi_fs::{FilesSorter, FolderStage};
use yazi_macro::{act, render, render_and, succ};
use yazi_parser::mgr::SortOpt;
use yazi_shared::data::Data;
use yazi_shared::{data::Data, path::PathLike};
use crate::{Actor, Ctx};
@ -23,7 +23,7 @@ impl Actor for Sort {
pref.sort_translit = opt.translit.unwrap_or(pref.sort_translit);
let sorter = FilesSorter::from(&*pref);
let hovered = cx.hovered().map(|f| f.urn().to_owned());
let hovered = cx.hovered().map(|f| f.urn().owned());
let apply = |f: &mut Folder| {
if f.stage == FolderStage::Loading {
render!();
@ -49,7 +49,7 @@ impl Actor for Sort {
{
render!(h.repos(None));
act!(mgr:peek, cx, true)?;
} else if hovered.as_deref() != cx.hovered().map(|f| f.urn()) {
} else if cx.hovered().map(|f| f.urn()) != hovered.as_ref().map(Into::into) {
act!(mgr:peek, cx)?;
act!(mgr:watch, cx)?;
}

View file

@ -57,7 +57,7 @@ impl UpdateFiles {
let tab = cx.tab_mut();
let urn = tab.current.url.urn();
let leave = matches!(op, FilesOp::Deleting(_, ref urns) if urns.contains(urn));
let leave = matches!(op, FilesOp::Deleting(_, ref urns) if urns.contains(&urn));
if let Some(f) = tab.parent.as_mut() {
render!(f.update_pub(tab.id, op));
@ -102,7 +102,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().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)),
|(p, n)| matches!(op, FilesOp::Deleting(ref parent, ref urns) if *parent == p && urns.contains(&n)),
);
tab

View file

@ -108,10 +108,7 @@ impl UserData for Url {
me.ext().map(|s| lua.create_string(s.as_encoded_bytes())).transpose()
});
cached_field!(fields, parent, |_, me| Ok(me.parent().map(Self::new)));
cached_field!(fields, urn, |_, me| {
// FIXME: remove type inference
Ok(super::Path::<std::path::PathBuf>::new(me.urn()))
});
cached_field!(fields, urn, |_, me| Ok(super::Path::new(me.urn())));
cached_field!(fields, base, |_, me| Ok(me.base().map(Self::new)));
cached_field!(fields, scheme, |_, me| Ok(Scheme::new(&me.scheme)));
@ -168,8 +165,8 @@ impl UserData for Url {
});
methods.add_method("strip_prefix", |_, me, base: Value| {
let path = match base {
Value::String(s) => me.loc.strip_prefix(s.as_bytes().into_os_str()?).ok(),
Value::UserData(ud) => me.strip_prefix(&*ud.borrow::<Self>()?).map(AsRef::as_ref),
Value::String(s) => me.loc().strip_prefix(&s.as_bytes().into_os_str()?).map(Into::into),
Value::UserData(ud) => me.strip_prefix(&*ud.borrow::<Self>()?),
_ => Err("must be a string or Url".into_lua_err())?,
};
Ok(path.map(Self::new)) // TODO: return `Path` instead of `Url`

View file

@ -1,30 +1,21 @@
use std::{ops::Deref, path::PathBuf};
use std::ops::Deref;
use mlua::{ExternalError, FromLua, Lua, UserData, Value};
use yazi_shared::path::PathBufLike;
use yazi_shared::path::PathBufDyn;
pub struct Path<P: PathBufLike = PathBuf>(pub P);
pub struct Path(pub PathBufDyn);
impl<P> Deref for Path<P>
where
P: PathBufLike,
{
type Target = P;
impl Deref for Path {
type Target = PathBufDyn;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl<P> Path<P>
where
P: PathBufLike,
{
pub fn new(urn: impl Into<P>) -> Self { Self(urn.into()) }
impl Path {
pub fn new(urn: impl Into<PathBufDyn>) -> Self { Self(urn.into()) }
}
impl<P> FromLua for Path<P>
where
P: PathBufLike,
{
impl FromLua for Path {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
Ok(match value {
Value::UserData(ud) => ud.take()?,
@ -33,4 +24,4 @@ where
}
}
impl<P> UserData for Path<P> where P: PathBufLike {}
impl UserData for Path {}

View file

@ -4,13 +4,13 @@ use futures::executor::block_on;
use hashbrown::HashSet;
use serde::Serialize;
use yazi_fs::{CWD, Xdg, path::expand_url};
use yazi_shared::url::{UrlBuf, UrlCow, UrlLike};
use yazi_shared::{path::{PathBufDyn, PathLike}, url::{UrlBuf, UrlCow, UrlLike}};
use yazi_vfs::provider;
#[derive(Debug, Default, Serialize)]
pub struct Boot {
pub cwds: Vec<UrlBuf>,
pub files: Vec<PathBuf>,
pub files: Vec<PathBufDyn>,
pub local_events: HashSet<String>,
pub remote_events: HashSet<String>,
@ -22,25 +22,25 @@ pub struct Boot {
}
impl Boot {
async fn parse_entries(entries: &[UrlBuf]) -> (Vec<UrlBuf>, Vec<PathBuf>) {
async fn parse_entries(entries: &[UrlBuf]) -> (Vec<UrlBuf>, Vec<PathBufDyn>) {
if entries.is_empty() {
return (vec![CWD.load().as_ref().clone()], vec![PathBuf::default()]);
return (vec![CWD.load().as_ref().clone()], vec![PathBufDyn::os_default()]);
}
async fn go(entry: &UrlBuf) -> (UrlBuf, PathBuf) {
async fn go(entry: &UrlBuf) -> (UrlBuf, PathBufDyn) {
let mut entry = expand_url(entry);
if let Ok(u @ UrlCow::Owned { .. }) = provider::absolute(&entry).await {
entry = u.into_owned();
}
let Some((parent, child)) = entry.pair() else {
return (entry, PathBuf::default());
return (entry, PathBufDyn::os_default());
};
if provider::metadata(&entry).await.is_ok_and(|m| m.is_file()) {
(parent.into(), child.to_owned())
(parent.into(), child.owned())
} else {
(entry, PathBuf::default())
(entry, PathBufDyn::os_default())
}
}

View file

@ -2,7 +2,7 @@ use anyhow::{Result, bail};
use serde::Deserialize;
use yazi_codegen::DeserializeOver2;
use yazi_fs::{CWD, SortBy};
use yazi_shared::{SyncCell, url::{UrlBuf, UrlLike}};
use yazi_shared::{SyncCell, path::PathLike, url::{UrlBuf, UrlLike}};
use super::{MgrRatio, MouseEvents};
@ -33,8 +33,8 @@ impl Mgr {
}
let home = UrlBuf::from(dirs::home_dir().unwrap_or_default());
let cwd = if let Some(u) = CWD.load().strip_prefix(&home) {
format!("~{}{}", std::path::MAIN_SEPARATOR, u.display())
let cwd = if let Some(p) = CWD.load().strip_prefix(&home) {
format!("~{}{}", std::path::MAIN_SEPARATOR, p.display())
} else {
format!("{}", CWD.load().display())
};

View file

@ -1,15 +1,13 @@
use std::path::{Path, PathBuf};
use anyhow::Result;
use hashbrown::HashMap;
use yazi_fs::{Files, Filter, FilterCase};
use yazi_shared::url::UrlBuf;
use yazi_shared::{path::{AsPathDyn, PathBufDyn, PathLike}, url::UrlBuf};
use crate::tab::Folder;
pub struct Finder {
pub filter: Filter,
pub matched: HashMap<PathBuf, u8>,
pub matched: HashMap<PathBufDyn, u8>,
lock: FinderLock,
}
@ -64,7 +62,7 @@ impl Finder {
continue;
}
self.matched.insert(file.urn().to_owned(), i);
self.matched.insert(file.urn().owned(), i);
if self.matched.len() > 99 {
break;
}
@ -78,8 +76,11 @@ impl Finder {
}
impl Finder {
pub fn matched_idx(&self, folder: &Folder, urn: &Path) -> Option<u8> {
if self.lock == *folder { self.matched.get(urn).copied() } else { None }
pub fn matched_idx<T>(&self, folder: &Folder, urn: T) -> Option<u8>
where
T: AsPathDyn,
{
if self.lock == *folder { self.matched.get(&urn.as_path_dyn()).copied() } else { None }
}
}

View file

@ -1,4 +1,4 @@
use std::{mem, path::{Path, PathBuf}};
use std::mem;
use yazi_config::{LAYOUT, YAZI};
use yazi_dds::Pubsub;
@ -6,7 +6,7 @@ use yazi_fs::{File, Files, FilesOp, FolderStage, cha::Cha};
use yazi_macro::err;
use yazi_parser::Step;
use yazi_proxy::MgrProxy;
use yazi_shared::{Id, url::UrlBuf};
use yazi_shared::{Id, path::{PathBufDyn, PathBufLike, PathDyn, PathLike}, url::UrlBuf};
use yazi_widgets::Scrollable;
pub struct Folder {
@ -19,7 +19,7 @@ pub struct Folder {
pub cursor: usize,
pub page: usize,
pub trace: Option<PathBuf>,
pub trace: Option<PathBufDyn>,
}
impl Default for Folder {
@ -101,14 +101,14 @@ impl Folder {
self.scroll(step)
};
self.trace = self.hovered().filter(|_| b).map(|h| h.urn().to_owned()).or(self.trace.take());
self.trace = self.hovered().filter(|_| b).map(|h| h.urn().owned()).or(self.trace.take());
b |= self.squeeze_offset();
self.sync_page(false);
b
}
pub fn hover(&mut self, urn: &Path) -> bool {
pub fn hover(&mut self, urn: PathDyn) -> bool {
if self.hovered().map(|h| h.urn()) == Some(urn) {
return self.arrow(0);
}
@ -117,11 +117,11 @@ impl Folder {
self.arrow(new - self.cursor as isize)
}
pub fn repos(&mut self, urn: Option<&Path>) -> bool {
pub fn repos(&mut self, urn: Option<PathDyn>) -> bool {
if let Some(u) = urn {
self.hover(u)
} else if let Some(u) = &self.trace {
self.hover(&u.clone())
self.hover(u.clone().borrow())
} else {
self.arrow(0)
}

View file

@ -52,7 +52,9 @@ impl Tasks {
let loading = self.scheduler.prework.sizing.read();
targets
.iter()
.filter(|f| f.is_dir() && !targets.sizes.contains_key(f.urn()) && !loading.contains(&f.url))
.filter(|f| {
f.is_dir() && !targets.sizes.contains_key(&f.urn()) && !loading.contains(&f.url)
})
.map(|f| &f.url)
.collect()
};

View file

@ -46,8 +46,8 @@ impl Sendable {
Some(t) if t == TypeId::of::<yazi_binding::Url>() => {
Data::Url(ud.take::<yazi_binding::Url>()?.into())
}
Some(t) if t == TypeId::of::<yazi_binding::Path<std::path::PathBuf>>() => {
Data::Path(ud.take::<yazi_binding::Path<std::path::PathBuf>>()?.0)
Some(t) if t == TypeId::of::<yazi_binding::Path>() => {
Data::Path(ud.take::<yazi_binding::Path>()?.0)
}
Some(t) if t == TypeId::of::<yazi_binding::Id>() => {
Data::Id(**ud.borrow::<yazi_binding::Id>()?)
@ -83,7 +83,7 @@ impl Sendable {
Value::Table(tbl)
}
Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::<std::path::PathBuf>::new(u).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
Data::Any(a) => {
if a.is::<yazi_fs::FilesOp>() {
lua.create_any_userdata(*a.downcast::<yazi_fs::FilesOp>().unwrap())?.into_lua(lua)?
@ -121,7 +121,7 @@ impl Sendable {
}
Data::Id(i) => yazi_binding::Id(*i).into_lua(lua)?,
Data::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::<std::path::PathBuf>::new(u).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_string(b)?),
Data::Any(a) => {
if let Some(t) = a.downcast_ref::<yazi_fs::FilesOp>() {
@ -230,7 +230,7 @@ impl Sendable {
fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result<Value> {
match key {
DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua),
DataKey::Path(u) => yazi_binding::Path::<std::path::PathBuf>::new(u).into_lua(lua),
DataKey::Path(u) => yazi_binding::Path::new(u).into_lua(lua),
_ => Self::key_to_value_ref(lua, &key),
}
}
@ -244,7 +244,7 @@ impl Sendable {
DataKey::String(s) => Value::String(lua.create_string(s.as_ref())?),
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::<std::path::PathBuf>::new(u).into_lua(lua)?,
DataKey::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
DataKey::Bytes(b) => Value::String(lua.create_string(b)?),
})
}

View file

@ -3,7 +3,7 @@ use yazi_actor::Ctx;
use yazi_boot::BOOT;
use yazi_macro::act;
use yazi_parser::{VoidOpt, mgr::CdSource};
use yazi_shared::{data::Data, url::UrlLike};
use yazi_shared::{data::Data, path::PathBufLike, url::UrlLike};
use crate::app::App;
@ -18,7 +18,7 @@ impl App {
let cx = &mut Ctx::active(&mut self.core);
cx.tab = i;
if file.as_os_str().is_empty() {
if file.is_empty() {
act!(mgr:cd, cx, (BOOT.cwds[i].clone(), CdSource::Tab))?;
} else {
act!(mgr:reveal, cx, (BOOT.cwds[i].join(file), CdSource::Tab))?;

View file

@ -40,7 +40,7 @@ impl File {
pub fn uri(&self) -> PathDyn<'_> { self.url.uri() }
#[inline]
pub fn urn(&self) -> &Path { self.url.urn() }
pub fn urn(&self) -> PathDyn<'_> { self.url.urn() }
#[inline]
pub fn name(&self) -> Option<&OsStr> { self.url.name() }

View file

@ -1,7 +1,7 @@
use std::{mem, ops::{Deref, DerefMut, Not}, path::{Path, PathBuf}};
use std::{mem, ops::{Deref, DerefMut, Not}};
use hashbrown::{HashMap, HashSet};
use yazi_shared::Id;
use yazi_shared::{Id, path::{PathBufDyn, PathBufLike, PathDyn, PathLike}};
use super::{FilesSorter, Filter};
use crate::{FILES_TICKET, File, SortBy};
@ -14,7 +14,7 @@ pub struct Files {
version: u64,
pub revision: u64,
pub sizes: HashMap<PathBuf, u64>,
pub sizes: HashMap<PathBufDyn, u64>,
sorter: FilesSorter,
filter: Option<Filter>,
@ -69,7 +69,7 @@ impl Files {
}
}
pub fn update_size(&mut self, mut sizes: HashMap<PathBuf, u64>) {
pub fn update_size(&mut self, mut sizes: HashMap<PathBufDyn, u64>) {
if sizes.len() <= 50 {
sizes.retain(|k, v| self.sizes.get(k) != Some(v));
}
@ -97,9 +97,9 @@ impl Files {
macro_rules! go {
($dist:expr, $src:expr, $inc:literal) => {
let mut todo: HashMap<_, _> = $src.into_iter().map(|f| (f.urn().to_owned(), f)).collect();
let mut todo: HashMap<_, _> = $src.into_iter().map(|f| (f.urn().owned(), f)).collect();
for f in &$dist {
if todo.remove(f.urn()).is_some() && todo.is_empty() {
if todo.remove(&f.urn()).is_some() && todo.is_empty() {
break;
}
}
@ -120,25 +120,26 @@ impl Files {
}
#[cfg(unix)]
pub fn update_deleting(&mut self, urns: HashSet<PathBuf>) -> Vec<usize> {
use yazi_shared::path::PathLike;
pub fn update_deleting(&mut self, urns: HashSet<PathBufDyn>) -> Vec<usize> {
if urns.is_empty() {
return vec![];
}
let (mut hidden, mut items) = if let Some(filter) = &self.filter {
urns.into_iter().partition(|u| (!self.show_hidden && u.is_hidden()) || !filter.matches(u))
urns
.into_iter()
.partition(|u| (!self.show_hidden && u.borrow().is_hidden()) || !filter.matches(u))
} else if self.show_hidden {
(HashSet::new(), urns)
} else {
urns.into_iter().partition(|u| u.is_hidden())
urns.into_iter().partition(|u| u.borrow().is_hidden())
};
let mut deleted = Vec::with_capacity(items.len());
if !items.is_empty() {
let mut i = 0;
self.items.retain(|f| {
let b = items.remove(f.urn());
let b = items.remove(&f.urn());
if b {
deleted.push(i);
}
@ -147,7 +148,7 @@ impl Files {
});
}
if !hidden.is_empty() {
self.hidden.retain(|f| !hidden.remove(f.urn()));
self.hidden.retain(|f| !hidden.remove(&f.urn()));
}
self.revision += deleted.is_empty().not() as u64;
@ -155,12 +156,12 @@ impl Files {
}
#[cfg(windows)]
pub fn update_deleting(&mut self, mut urns: HashSet<PathBuf>) -> Vec<usize> {
pub fn update_deleting(&mut self, mut urns: HashSet<PathBufDyn>) -> Vec<usize> {
let mut deleted = Vec::with_capacity(urns.len());
if !urns.is_empty() {
let mut i = 0;
self.items.retain(|f| {
let b = urns.remove(f.urn());
let b = urns.remove(&f.urn());
if b {
deleted.push(i)
}
@ -169,7 +170,7 @@ impl Files {
});
}
if !urns.is_empty() {
self.hidden.retain(|f| !urns.remove(f.urn()));
self.hidden.retain(|f| !urns.remove(&f.urn()));
}
self.revision += deleted.is_empty().not() as u64;
@ -178,8 +179,8 @@ impl Files {
pub fn update_updating(
&mut self,
files: HashMap<PathBuf, File>,
) -> (HashMap<PathBuf, File>, HashMap<PathBuf, File>) {
files: HashMap<PathBufDyn, File>,
) -> (HashMap<PathBufDyn, File>, HashMap<PathBufDyn, File>) {
if files.is_empty() {
return Default::default();
}
@ -188,7 +189,7 @@ impl Files {
($dist:expr, $src:expr, $inc:literal) => {
let mut b = true;
for i in 0..$dist.len() {
if let Some(f) = $src.remove($dist[i].urn()) {
if let Some(f) = $src.remove(&$dist[i].urn()) {
b = b && $dist[i].cha.hits(f.cha);
b = b && $dist[i].urn() == f.urn();
@ -221,13 +222,13 @@ impl Files {
(hidden, items)
}
pub fn update_upserting(&mut self, files: HashMap<PathBuf, File>) {
pub fn update_upserting(&mut self, files: HashMap<PathBufDyn, File>) {
if files.is_empty() {
return;
}
self.update_deleting(
files.iter().filter(|&(u, f)| u != f.urn()).map(|(_, f)| f.urn().to_owned()).collect(),
files.iter().filter(|&(u, f)| u != f.urn()).map(|(_, f)| f.urn().owned()).collect(),
);
let (hidden, items) = self.update_updating(files);
@ -270,7 +271,7 @@ impl Files {
impl Files {
// --- Items
#[inline]
pub fn position(&self, urn: &Path) -> Option<usize> { self.iter().position(|f| urn == f.urn()) }
pub fn position(&self, urn: PathDyn) -> Option<usize> { self.iter().position(|f| urn == f.urn()) }
// --- Ticket
#[inline]

View file

@ -1,8 +1,8 @@
use std::path::{Path, PathBuf};
use std::path::Path;
use hashbrown::{HashMap, HashSet};
use yazi_macro::relay;
use yazi_shared::{Id, Ids, url::{UrlBuf, UrlLike}};
use yazi_shared::{Id, Ids, path::{PathBufDyn, PathLike}, url::{UrlBuf, UrlLike}};
use super::File;
use crate::{cha::Cha, error::Error};
@ -14,13 +14,13 @@ pub enum FilesOp {
Full(UrlBuf, Vec<File>, Cha),
Part(UrlBuf, Vec<File>, Id),
Done(UrlBuf, Cha, Id),
Size(UrlBuf, HashMap<PathBuf, u64>),
Size(UrlBuf, HashMap<PathBufDyn, u64>),
IOErr(UrlBuf, Error),
Creating(UrlBuf, Vec<File>),
Deleting(UrlBuf, HashSet<PathBuf>),
Updating(UrlBuf, HashMap<PathBuf, File>),
Upserting(UrlBuf, HashMap<PathBuf, File>),
Deleting(UrlBuf, HashSet<PathBufDyn>),
Updating(UrlBuf, HashMap<PathBufDyn, File>),
Upserting(UrlBuf, HashMap<PathBufDyn, File>),
}
impl FilesOp {
@ -57,10 +57,10 @@ impl FilesOp {
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().1.insert(o.urn().to_owned(), n);
parents.entry_ref(&o_p).or_default().1.insert(o.urn().owned(), n);
} else {
parents.entry_ref(&o_p).or_default().0.insert(o.urn().to_owned());
parents.entry_ref(&n_p).or_default().1.insert(n.urn().to_owned(), n);
parents.entry_ref(&o_p).or_default().0.insert(o.urn().owned());
parents.entry_ref(&n_p).or_default().1.insert(n.urn().owned(), n);
}
}
for (p, (o, n)) in parents {

View file

@ -1,7 +1,7 @@
use std::{cmp::Ordering, path::PathBuf};
use std::cmp::Ordering;
use hashbrown::HashMap;
use yazi_shared::{LcgRng, natsort, path::PathLike, translit::Transliterator, url::UrlLike};
use yazi_shared::{LcgRng, natsort, path::{PathBufDyn, PathLike}, translit::Transliterator, url::UrlLike};
use crate::{File, SortBy};
@ -15,7 +15,7 @@ pub struct FilesSorter {
}
impl FilesSorter {
pub(super) fn sort(&self, items: &mut [File], sizes: &HashMap<PathBuf, u64>) {
pub(super) fn sort(&self, items: &mut [File], sizes: &HashMap<PathBufDyn, u64>) {
if items.is_empty() {
return;
}
@ -53,8 +53,8 @@ impl FilesSorter {
SortBy::Alphabetical => items.sort_unstable_by(by_alphabetical),
SortBy::Natural => self.sort_naturally(items),
SortBy::Size => items.sort_unstable_by(|a, b| {
let aa = if a.is_dir() { sizes.get(a.urn()).copied() } else { None };
let bb = if b.is_dir() { sizes.get(b.urn()).copied() } else { None };
let aa = if a.is_dir() { sizes.get(&a.urn()).copied() } else { None };
let bb = if b.is_dir() { sizes.get(&b.urn()).copied() } else { None };
let ord = self.cmp(aa.unwrap_or(a.len), bb.unwrap_or(b.len), self.promote(a, b));
if ord == Ordering::Equal { by_alphabetical(a, b) } else { ord }
}),

View file

@ -1,14 +1,13 @@
use std::path::PathBuf;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_shared::path::PathBufDyn;
#[derive(Debug, Default)]
pub struct HoverOpt {
pub urn: Option<PathBuf>,
pub urn: Option<PathBufDyn>,
}
impl From<Option<PathBuf>> for HoverOpt {
fn from(urn: Option<PathBuf>) -> Self { Self { urn } }
impl From<Option<PathBufDyn>> for HoverOpt {
fn from(urn: Option<PathBufDyn>) -> Self { Self { urn } }
}
impl FromLua for HoverOpt {

View file

@ -10,7 +10,7 @@ use tracing::error;
use yazi_config::Priority;
use yazi_fs::{FilesOp, FsHash64};
use yazi_plugin::isolate;
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlLike}};
use yazi_shared::{event::CmdCow, path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_vfs::provider;
use super::{PreworkInFetch, PreworkInLoad, PreworkInSize};
@ -106,7 +106,7 @@ impl Prework {
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))),
HashMap::from_iter(buf.into_iter().map(|(u, s)| (u.urn().owned(), s))),
)
.emit();
});

View file

@ -7,7 +7,7 @@ authors = [ "sxyazi <sxyazi@gmail.com>" ]
description = "Yazi shared library"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
rust-version = "1.88.0"
rust-version = "1.91.0"
[dependencies]
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }

View file

@ -1,10 +1,10 @@
use std::{any::Any, borrow::Cow, path::PathBuf};
use std::{any::Any, borrow::Cow};
use anyhow::{Result, bail};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use crate::{Id, SStr, data::DataKey, url::{UrlBuf, UrlCow}};
use crate::{Id, SStr, data::DataKey, path::PathBufDyn, url::{UrlBuf, UrlCow}};
// --- Data
#[derive(Debug, Deserialize, Serialize)]
@ -21,7 +21,7 @@ pub enum Data {
#[serde(skip_deserializing)]
Url(UrlBuf),
#[serde(skip_deserializing)]
Path(PathBuf),
Path(PathBufDyn),
#[serde(skip)]
Bytes(Vec<u8>),
#[serde(skip)]

View file

@ -1,9 +1,9 @@
use std::{borrow::Cow, path::PathBuf};
use std::borrow::Cow;
use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize, de};
use crate::{Id, SStr, url::{UrlBuf, UrlCow}};
use crate::{Id, SStr, path::PathBufDyn, url::{UrlBuf, UrlCow}};
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(untagged)]
@ -18,7 +18,7 @@ pub enum DataKey {
#[serde(skip_deserializing)]
Url(UrlBuf),
#[serde(skip_deserializing)]
Path(PathBuf),
Path(PathBufDyn),
#[serde(skip)]
Bytes(Vec<u8>),
}

View file

@ -2,7 +2,7 @@ use std::{cmp, ffi::OsStr, fmt::{self, Debug, Formatter}, hash::{Hash, Hasher},
use anyhow::Result;
use crate::{loc::Loc, path::{AsInnerView, AsPathView, PathBufLike, PathLike}};
use crate::{loc::Loc, path::{AsInnerView, AsPathDyn, AsPathView, PathBufLike, PathDyn, PathLike}};
#[derive(Clone, Default, Eq)]
pub struct LocBuf<P: PathBufLike = PathBuf> {
@ -25,6 +25,20 @@ impl AsRef<std::path::Path> for LocBuf<PathBuf> {
fn as_ref(&self) -> &std::path::Path { self.inner.as_ref() }
}
impl<T> AsPathDyn for LocBuf<T>
where
T: PathBufLike + AsPathDyn,
{
fn as_path_dyn(&self) -> PathDyn<'_> { self.inner.as_path_dyn() }
}
impl<T> AsPathDyn for &LocBuf<T>
where
T: PathBufLike + AsPathDyn,
{
fn as_path_dyn(&self) -> PathDyn<'_> { self.inner.as_path_dyn() }
}
impl<P> PartialEq for LocBuf<P>
where
P: PathBufLike + PartialEq,
@ -117,10 +131,6 @@ where
Ok(Self { inner: loc.inner, uri, urn })
}
// FIXME: use `LocBuf::empty()` when Rust 1.91.0 released
// pub const fn empty() -> Self { Self { inner: PathBuf::new(), uri: 0, urn: 0 }
// }
pub fn zeroed<T>(path: T) -> Self
where
T: Into<P>,
@ -245,6 +255,10 @@ where
pub fn ext(&self) -> Option<<P::Borrowed<'_> as PathLike<'_>>::Inner> { self.as_loc().ext() }
}
impl LocBuf<PathBuf> {
pub const fn empty() -> Self { Self { inner: PathBuf::new(), uri: 0, urn: 0 } }
}
#[cfg(test)]
mod tests {
use std::path::Path;

View file

@ -2,7 +2,7 @@ use std::{hash::{Hash, Hasher}, marker::PhantomData, ops::Deref, path::Path};
use anyhow::{Result, bail};
use crate::{loc::LocBuf, path::{AsPathView, PathBufLike, PathInner, PathLike, ToPathOwned}};
use crate::{loc::LocBuf, path::{AsPathView, PathBufLike, PathInner, PathLike}};
#[derive(Clone, Copy, Debug)]
pub struct Loc<'p, P = &'p Path> {
@ -43,11 +43,11 @@ where
impl<'p, P> From<Loc<'p, P>> for LocBuf<<P as PathLike<'p>>::Owned>
where
P: PathLike<'p> + ToPathOwned<<P as PathLike<'p>>::Owned>,
P: PathLike<'p>,
<P as PathLike<'p>>::Owned: PathBufLike,
{
fn from(value: Loc<'p, P>) -> Self {
Self { inner: value.inner.to_path_owned(), uri: value.uri, urn: value.urn }
Self { inner: value.inner.owned(), uri: value.uri, urn: value.urn }
}
}

View file

@ -10,12 +10,16 @@ where
type InnerRef<'a>: PathInner<'a>;
type Borrowed<'a>: PathLike<'a> + AsPathView<'a, Self::Borrowed<'a>> + Debug + Hash;
fn borrow(&self) -> Self::Borrowed<'_>;
fn encoded_bytes(&self) -> &[u8];
unsafe fn from_encoded_bytes(bytes: Vec<u8>) -> Self;
fn into_encoded_bytes(self) -> Vec<u8>;
fn is_empty(&self) -> bool { self.encoded_bytes().is_empty() }
fn len(&self) -> usize { self.encoded_bytes().len() }
fn set_file_name<T>(&mut self, name: T)
@ -30,6 +34,8 @@ impl PathBufLike for std::path::PathBuf {
type Inner = std::ffi::OsString;
type InnerRef<'a> = &'a std::ffi::OsStr;
fn borrow(&self) -> Self::Borrowed<'_> { self.as_path() }
fn encoded_bytes(&self) -> &[u8] { self.as_os_str().as_encoded_bytes() }
unsafe fn from_encoded_bytes(bytes: Vec<u8>) -> Self {

View file

@ -1,3 +1,6 @@
use hashbrown::Equivalent;
use serde::Serialize;
use super::{AsInnerView, AsPathView};
use crate::path::{PathBufLike, PathLike};
@ -18,8 +21,13 @@ impl<'a> From<&'a std::path::Path> for PathDyn<'a> {
fn from(value: &'a std::path::Path) -> Self { PathDyn::Os(value) }
}
impl<'a> From<&'a PathBufDyn> for PathDyn<'a> {
fn from(value: &'a PathBufDyn) -> Self { value.borrow() }
}
impl<'p> PathLike<'p> for PathDyn<'p> {
type Components<'a> = std::path::Components<'a>;
type Display<'a> = std::path::Display<'a>;
type Inner = &'p [u8];
type Owned = PathBufDyn;
type View<'a> = PathDyn<'a>;
@ -33,6 +41,12 @@ impl<'p> PathLike<'p> for PathDyn<'p> {
// FIXME: remove
fn default() -> Self { Self::Os(std::path::Path::new("")) }
fn display(self) -> Self::Display<'p> {
match self {
Self::Os(p) => p.display(),
}
}
fn encoded_bytes(self) -> &'p [u8] {
match self {
Self::Os(p) => p.as_os_str().as_encoded_bytes(),
@ -71,6 +85,12 @@ impl<'p> PathLike<'p> for PathDyn<'p> {
}
}
fn owned(self) -> Self::Owned {
match self {
Self::Os(p) => Self::Owned::Os(p.to_path_buf()),
}
}
fn parent(self) -> Option<Self> {
Some(match self {
Self::Os(p) => Self::Os(p.parent()?),
@ -87,16 +107,40 @@ impl<'p> PathLike<'p> for PathDyn<'p> {
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
impl PartialEq<PathBufDyn> for PathDyn<'_> {
fn eq(&self, other: &PathBufDyn) -> bool { *self == other.borrow() }
}
impl PartialEq<PathDyn<'_>> for &std::path::Path {
fn eq(&self, other: &PathDyn<'_>) -> bool { matches!(*other, PathDyn::Os(p) if p == *self) }
}
impl Equivalent<PathBufDyn> for PathDyn<'_> {
fn equivalent(&self, key: &PathBufDyn) -> bool { *self == key.borrow() }
}
// --- PathBufDyn
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(untagged)]
pub enum PathBufDyn {
Os(std::path::PathBuf),
}
impl PathBufDyn {
pub const fn os_default() -> Self { Self::Os(std::path::PathBuf::new()) }
}
impl PathBufLike for PathBufDyn {
type Borrowed<'a> = PathDyn<'a>;
type Inner = Vec<u8>;
type InnerRef<'a> = &'a [u8];
fn borrow(&self) -> Self::Borrowed<'_> {
match self {
Self::Os(p) => Self::Borrowed::Os(p.as_path()),
}
}
fn encoded_bytes(&self) -> &[u8] {
match self {
Self::Os(p) => p.as_os_str().as_encoded_bytes(),
@ -130,3 +174,27 @@ impl PathBufLike for PathBufDyn {
}
}
}
impl From<PathDyn<'_>> for PathBufDyn {
fn from(value: PathDyn<'_>) -> Self {
match value {
PathDyn::Os(p) => Self::Os(p.to_path_buf()),
}
}
}
impl From<&PathBufDyn> for PathBufDyn {
fn from(value: &PathBufDyn) -> Self { value.clone() }
}
impl PartialEq<PathDyn<'_>> for PathBufDyn {
fn eq(&self, other: &PathDyn<'_>) -> bool { self.borrow() == *other }
}
impl PartialEq<PathDyn<'_>> for &PathBufDyn {
fn eq(&self, other: &PathDyn<'_>) -> bool { self.borrow() == *other }
}
impl Equivalent<PathDyn<'_>> for PathBufDyn {
fn equivalent(&self, key: &PathDyn<'_>) -> bool { self.borrow() == *key }
}

View file

@ -1,7 +1,9 @@
pub trait PathInner<'a>: Copy {
fn len(self) -> usize { self.encoded_bytes().len() }
fn encoded_bytes(self) -> &'a [u8];
fn is_empty(self) -> bool { self.encoded_bytes().is_empty() }
fn len(self) -> usize { self.encoded_bytes().len() }
}
impl<'a> PathInner<'a> for &'a std::ffi::OsStr {

View file

@ -8,11 +8,14 @@ where
type Owned: PathBufLike + Into<Self::Owned>;
type View<'a>;
type Components<'a>: AsPathView<'a, Self::View<'a>> + Clone + DoubleEndedIterator;
type Display<'a>: std::fmt::Display;
fn components(self) -> Self::Components<'p>;
fn default() -> Self;
fn display(self) -> Self::Display<'p>;
fn encoded_bytes(self) -> &'p [u8];
fn extension(self) -> Option<Self::Inner>;
@ -27,7 +30,7 @@ where
#[cfg(unix)]
fn is_hidden(self) -> bool {
self.file_name().map_or(false, |n| n.encoded_bytes().get(0) == Some(&b'.'))
self.file_name().is_some_and(|n| n.encoded_bytes().first() == Some(&b'.'))
}
fn join<'a, T>(self, base: T) -> Self::Owned
@ -36,6 +39,8 @@ where
fn len(self) -> usize { self.encoded_bytes().len() }
fn owned(self) -> Self::Owned;
fn parent(self) -> Option<Self>;
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
@ -45,6 +50,7 @@ where
impl<'p> PathLike<'p> for &'p std::path::Path {
type Components<'a> = std::path::Components<'a>;
type Display<'a> = std::path::Display<'a>;
type Inner = &'p std::ffi::OsStr;
type Owned = std::path::PathBuf;
type View<'a> = &'a std::path::Path;
@ -53,6 +59,8 @@ impl<'p> PathLike<'p> for &'p std::path::Path {
fn default() -> Self { std::path::Path::new("") }
fn display(self) -> Self::Display<'p> { self.display() }
fn encoded_bytes(self) -> &'p [u8] { self.as_os_str().as_encoded_bytes() }
fn extension(self) -> Option<Self::Inner> { self.extension() }
@ -72,6 +80,8 @@ impl<'p> PathLike<'p> for &'p std::path::Path {
self.join(base.as_path_view())
}
fn owned(self) -> Self::Owned { self.to_path_buf() }
fn parent(self) -> Option<Self> { self.parent() }
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>

View file

@ -1,23 +1,87 @@
use crate::path::PathLike;
use std::{borrow::Cow, ffi::{OsStr, OsString}};
use super::{PathBufDyn, PathDyn};
use crate::path::{PathBufLike, PathLike};
pub trait AsPath {
fn as_path(&self) -> impl PathLike<'_>;
}
impl AsPath for &std::ffi::OsStr {
impl AsPath for OsStr {
fn as_path(&self) -> impl PathLike<'_> { std::path::Path::new(self) }
}
impl AsPath for &std::path::Path {
fn as_path(&self) -> impl PathLike<'_> { *self }
impl AsPath for &OsStr {
fn as_path(&self) -> impl PathLike<'_> { std::path::Path::new(self) }
}
impl AsPath for std::path::Path {
fn as_path(&self) -> impl PathLike<'_> { self }
}
impl AsPath for std::path::PathBuf {
fn as_path(&self) -> impl PathLike<'_> { self.as_path() }
}
impl AsPath for &std::path::PathBuf {
fn as_path(&self) -> impl PathLike<'_> { (*self).as_path() }
impl AsPath for PathDyn<'_> {
fn as_path(&self) -> impl PathLike<'_> { *self }
}
impl AsPath for PathBufDyn {
fn as_path(&self) -> impl PathLike<'_> { self.borrow() }
}
impl AsPath for &PathBufDyn {
fn as_path(&self) -> impl PathLike<'_> { self.borrow() }
}
// --- AsPathDyn
pub trait AsPathDyn {
fn as_path_dyn(&self) -> PathDyn<'_>;
}
impl AsPathDyn for &str {
fn as_path_dyn(&self) -> PathDyn<'_> { std::path::Path::new(self).into() }
}
impl AsPathDyn for String {
fn as_path_dyn(&self) -> PathDyn<'_> { std::path::Path::new(self).into() }
}
impl AsPathDyn for &String {
fn as_path_dyn(&self) -> PathDyn<'_> { std::path::Path::new(self).into() }
}
impl AsPathDyn for OsStr {
fn as_path_dyn(&self) -> PathDyn<'_> { std::path::Path::new(self).into() }
}
impl AsPathDyn for &OsStr {
fn as_path_dyn(&self) -> PathDyn<'_> { std::path::Path::new(self).into() }
}
impl AsPathDyn for Cow<'_, OsStr> {
fn as_path_dyn(&self) -> PathDyn<'_> { std::path::Path::new(self).into() }
}
impl AsPathDyn for std::path::PathBuf {
fn as_path_dyn(&self) -> PathDyn<'_> { self.as_path().into() }
}
impl AsPathDyn for &std::path::PathBuf {
fn as_path_dyn(&self) -> PathDyn<'_> { self.as_path().into() }
}
impl AsPathDyn for PathDyn<'_> {
fn as_path_dyn(&self) -> PathDyn<'_> { *self }
}
impl AsPathDyn for PathBufDyn {
fn as_path_dyn(&self) -> PathDyn<'_> { self.borrow() }
}
impl AsPathDyn for &PathBufDyn {
fn as_path_dyn(&self) -> PathDyn<'_> { self.borrow() }
}
// --- AsPathView
@ -29,6 +93,10 @@ impl<'a> AsPathView<'a, &'a std::path::Path> for &'a str {
fn as_path_view(self) -> &'a std::path::Path { std::path::Path::new(self) }
}
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 }
}
@ -41,45 +109,33 @@ 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> AsPathView<'a, super::PathDyn<'a>> for &'a super::PathBufDyn {
fn as_path_view(self) -> super::PathDyn<'a> {
impl<'a> AsPathView<'a, PathDyn<'a>> for &'a PathBufDyn {
fn as_path_view(self) -> PathDyn<'a> {
match self {
super::PathBufDyn::Os(p) => super::PathDyn::Os(p.as_path()),
PathBufDyn::Os(p) => PathDyn::Os(p.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 }
}
// --- ToPathOwned
pub trait ToPathOwned<T> {
fn to_path_owned(&self) -> T;
}
impl ToPathOwned<std::path::PathBuf> for std::path::Path {
fn to_path_owned(&self) -> std::path::PathBuf { self.to_owned() }
}
impl<T, U> ToPathOwned<U> for &T
where
T: ?Sized + ToPathOwned<U>,
{
fn to_path_owned(&self) -> U { (*self).to_path_owned() }
}
// --- AsInnerView
pub trait AsInnerView<'a, T> {
fn as_inner_view(&'a self) -> T;
}
impl<'a> AsInnerView<'a, &'a std::ffi::OsStr> for std::ffi::OsStr {
fn as_inner_view(&'a self) -> &'a std::ffi::OsStr { self }
impl<'a> AsInnerView<'a, &'a OsStr> for OsStr {
fn as_inner_view(&'a self) -> &'a OsStr { self }
}
impl<'a> AsInnerView<'a, &'a std::ffi::OsStr> for std::ffi::OsString {
fn as_inner_view(&'a self) -> &'a std::ffi::OsStr { self }
impl<'a> AsInnerView<'a, &'a OsStr> for OsString {
fn as_inner_view(&'a self) -> &'a OsStr { self }
}
impl<'a> AsInnerView<'a, &'a [u8]> for [u8] {

View file

@ -1,9 +1,9 @@
use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, path::{Path, PathBuf}, str::FromStr, sync::OnceLock};
use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, path::{Path, PathBuf}, str::FromStr};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use crate::{loc::LocBuf, pool::Pool, scheme::{Scheme, SchemeLike}, url::{AsUrl, Encode, EncodeTilded, Url, UrlCow}};
use crate::{loc::{Loc, LocBuf}, path::PathDyn, pool::Pool, scheme::{Scheme, SchemeLike}, url::{AsUrl, Encode, EncodeTilded, Url, UrlCow}};
#[derive(Clone, Default, Eq, Hash, PartialEq)]
pub struct UrlBuf {
@ -11,6 +11,18 @@ pub struct UrlBuf {
pub scheme: Scheme,
}
impl From<&UrlBuf> for UrlBuf {
fn from(url: &UrlBuf) -> Self { url.clone() }
}
impl From<Url<'_>> for UrlBuf {
fn from(url: Url<'_>) -> Self { Self { loc: url.loc.into(), scheme: url.scheme.into() } }
}
impl From<&Url<'_>> for UrlBuf {
fn from(url: &Url<'_>) -> Self { Self { loc: url.loc.into(), scheme: url.scheme.into() } }
}
impl From<LocBuf> for UrlBuf {
fn from(loc: LocBuf) -> Self { Self { loc, scheme: Scheme::Regular } }
}
@ -19,18 +31,6 @@ impl From<PathBuf> for UrlBuf {
fn from(path: PathBuf) -> Self { LocBuf::from(path).into() }
}
impl From<&Url<'_>> for UrlBuf {
fn from(url: &Url<'_>) -> Self { Self { loc: url.loc.into(), scheme: url.scheme.into() } }
}
impl From<Url<'_>> for UrlBuf {
fn from(url: Url<'_>) -> Self { Self { loc: url.loc.into(), scheme: url.scheme.into() } }
}
impl From<&Self> for UrlBuf {
fn from(url: &Self) -> Self { url.clone() }
}
impl From<&PathBuf> for UrlBuf {
fn from(path: &PathBuf) -> Self { path.to_owned().into() }
}
@ -39,6 +39,14 @@ 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;
@ -81,12 +89,8 @@ impl PartialEq<Url<'_>> for &UrlBuf {
impl UrlBuf {
#[inline]
pub fn new() -> &'static Self {
static U: OnceLock<UrlBuf> = OnceLock::new();
U.get_or_init(Self::default)
// FIXME: use `LocBuf::empty()` when Rust 1.91.0 released
// static U: UrlBuf = UrlBuf { loc: LocBuf::empty(), scheme: Scheme::Regular
// }; &U
static U: UrlBuf = UrlBuf { loc: LocBuf::empty(), scheme: Scheme::Regular };
&U
}
#[inline]
@ -104,6 +108,9 @@ impl UrlBuf {
}
impl UrlBuf {
#[inline]
pub fn loc(&self) -> Loc<'_> { self.loc.as_loc() }
// --- Regular
#[inline]
pub fn is_regular(&self) -> bool { self.as_url().is_regular() }

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
use crate::{loc::Loc, path::PathDyn, scheme::{AsScheme, SchemeRef}, url::{Components, Display, Url, UrlBuf, UrlCow}};
use crate::{loc::Loc, path::{AsPathDyn, PathDyn}, scheme::{AsScheme, SchemeRef}, url::{Components, Display, Url, UrlBuf, UrlCow}};
// --- AsUrl
pub trait AsUrl {
@ -27,6 +27,15 @@ 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 }
@ -101,13 +110,13 @@ where
fn is_absolute(&self) -> bool { self.as_url().is_absolute() }
fn join(&self, path: impl AsRef<Path>) -> UrlBuf { self.as_url().join(path) }
fn join(&self, path: impl AsPathDyn) -> UrlBuf { self.as_url().join(path) }
fn name(&self) -> Option<&OsStr> { self.as_url().name() }
fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
fn pair(&self) -> Option<(Url<'_>, &Path)> { self.as_url().pair() }
fn pair(&self) -> Option<(Url<'_>, PathDyn<'_>)> { self.as_url().pair() }
fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
@ -115,11 +124,13 @@ where
fn stem(&self) -> Option<&OsStr> { self.as_url().stem() }
fn strip_prefix(&self, base: impl AsUrl) -> Option<&Path> { self.as_url().strip_prefix(base) }
fn strip_prefix(&self, base: impl AsUrl) -> Option<PathDyn<'_>> {
self.as_url().strip_prefix(base)
}
fn uri(&self) -> PathDyn<'_> { self.as_url().uri() }
fn urn(&self) -> &Path { self.as_url().urn() }
fn urn(&self) -> PathDyn<'_> { self.as_url().urn() }
}
impl UrlLike for UrlBuf {}

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, path::{Path, PathBuf
use hashbrown::Equivalent;
use crate::{loc::{Loc, LocBuf}, path::{PathDyn, PathLike}, scheme::SchemeRef, url::{AsUrl, Components, Encode, UrlBuf}};
use crate::{loc::{Loc, LocBuf}, path::{AsPathDyn, PathDyn, PathLike}, scheme::SchemeRef, url::{AsUrl, Components, Encode, UrlBuf}};
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct Url<'a> {
@ -64,28 +64,30 @@ impl<'a> Url<'a> {
#[inline]
pub fn to_owned(self) -> UrlBuf { self.into() }
pub fn join(self, path: impl AsRef<Path>) -> UrlBuf {
pub fn join(self, path: impl AsPathDyn) -> UrlBuf {
use SchemeRef as S;
let join = self.loc.join(path);
let joined = match path.as_path_dyn() {
PathDyn::Os(p) => self.loc.join(p),
};
let loc = match self.scheme {
S::Regular => join.into(),
S::Search(_) => LocBuf::<PathBuf>::new(join, self.loc.base(), self.loc.base()),
S::Archive(_) => LocBuf::<PathBuf>::floated(join, self.loc.base()),
S::Sftp(_) => join.into(),
S::Regular => joined.into(),
S::Search(_) => LocBuf::<PathBuf>::new(joined, self.loc.base(), self.loc.base()),
S::Archive(_) => LocBuf::<PathBuf>::floated(joined, self.loc.base()),
S::Sftp(_) => joined.into(),
};
UrlBuf { loc, scheme: self.scheme.into() }
}
pub fn strip_prefix(self, base: impl AsUrl) -> Option<&'a Path> {
pub fn strip_prefix(self, base: impl AsUrl) -> Option<PathDyn<'a>> {
use SchemeRef as S;
let base = base.as_url();
let prefix = self.loc.strip_prefix(base.loc)?;
let prefix = self.loc.strip_prefix(base.loc)?.into();
Some(match (self.scheme, base.scheme) {
match (self.scheme, base.scheme) {
// Same scheme
(S::Regular, S::Regular) => Some(prefix),
(S::Search(_), S::Search(_)) => Some(prefix),
@ -109,14 +111,14 @@ impl<'a> Url<'a> {
(S::Sftp(_), S::Regular) => None,
(S::Sftp(_), S::Search(_)) => None,
(S::Sftp(_), S::Archive(_)) => None,
}?)
}
}
#[inline]
pub fn uri(self) -> PathDyn<'a> { self.loc.uri().into() }
#[inline]
pub fn urn(self) -> &'a Path { self.loc.urn() }
pub fn urn(self) -> PathDyn<'a> { self.loc.urn().into() }
#[inline]
pub fn name(self) -> Option<&'a OsStr> { self.loc.name() }
@ -200,7 +202,7 @@ impl<'a> Url<'a> {
}
#[inline]
pub fn pair(self) -> Option<(Self, &'a Path)> { Some((self.parent()?, self.loc.urn())) }
pub fn pair(self) -> Option<(Self, PathDyn<'a>)> { Some((self.parent()?, self.loc.urn().into())) }
#[inline]
pub fn as_path(self) -> Option<&'a Path> {

View file

@ -1,5 +1,5 @@
use yazi_fs::FilesOp;
use yazi_shared::url::{UrlBuf, UrlLike};
use yazi_shared::{path::PathLike, url::{UrlBuf, UrlLike}};
use crate::maybe_exists;
@ -15,7 +15,7 @@ impl VfsFilesOp for FilesOp {
} else if maybe_exists(cwd).await {
Self::IOErr(cwd.clone(), err).emit();
} else if let Some((p, n)) = cwd.pair() {
Self::Deleting(p.into(), [n.to_owned()].into()).emit();
Self::Deleting(p.into(), [n.owned()].into()).emit();
}
}
}

View file

@ -7,7 +7,7 @@ use tokio::{pin, sync::mpsc::{self, UnboundedReceiver}};
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use tracing::error;
use yazi_fs::{File, FilesOp, provider};
use yazi_shared::url::{UrlBuf, UrlLike};
use yazi_shared::{path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_vfs::VfsFile;
use crate::{Reporter, WATCHER};
@ -70,18 +70,18 @@ impl Local {
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.to_owned()].into()));
ops.push(FilesOp::Deleting(parent.into(), [urn.owned()].into()));
continue;
};
if let Some(p) = file.url.as_path()
&& !provider::local::must_case_match(p).await
{
ops.push(FilesOp::Deleting(parent.into(), [urn.to_owned()].into()));
ops.push(FilesOp::Deleting(parent.into(), [urn.owned()].into()));
continue;
}
ops.push(FilesOp::Upserting(parent.into(), [(urn.to_owned(), file)].into()));
ops.push(FilesOp::Upserting(parent.into(), [(urn.owned(), file)].into()));
}
FilesOp::mutate(ops);

View file

@ -6,7 +6,7 @@ use tokio::{pin, sync::mpsc::UnboundedReceiver};
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_fs::{File, FilesOp};
use yazi_proxy::MgrProxy;
use yazi_shared::url::{Url, UrlBuf, UrlLike};
use yazi_shared::{path::PathLike, url::{Url, UrlBuf, UrlLike}};
use yazi_vfs::VfsFile;
use crate::{Reporter, WATCHER};
@ -39,14 +39,14 @@ impl Remote {
for u in urls {
let Some((parent, urn)) = u.pair() else { continue };
let Ok(mut file) = File::new(&u).await else {
ops.push(FilesOp::Deleting(parent.into(), [urn.to_owned()].into()));
ops.push(FilesOp::Deleting(parent.into(), [urn.owned()].into()));
continue;
};
let is_file = file.is_file();
file.cha.ctime = Some(SystemTime::now());
ops.push(FilesOp::Upserting(parent.into(), [(urn.to_owned(), file)].into()));
ops.push(FilesOp::Upserting(parent.into(), [(urn.owned(), file)].into()));
if is_file {
ups.push(u);
}

View file

@ -1,5 +1,5 @@
use tokio::sync::mpsc;
use yazi_shared::{scheme::SchemeRef, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
use yazi_shared::{path::PathLike, scheme::SchemeRef, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
use crate::{WATCHED, local::LINKED};