refactor: refine PathLike and StrandLike traits

This commit is contained in:
sxyazi 2025-11-19 13:38:52 +08:00
parent f9abe886cc
commit 2b79c3d30f
No known key found for this signature in database
109 changed files with 1282 additions and 1492 deletions

View file

@ -60,7 +60,10 @@ uzers = "0.12.1"
[workspace.lints.clippy]
format_push_string = "warn"
if_same_then_else = "allow"
implicit_clone = "warn"
len_without_is_empty = "allow"
missing_safety_doc = "allow"
module_inception = "allow"
unit_arg = "allow"
use_self = "warn"

View file

@ -3,7 +3,7 @@ use std::{mem, ops::ControlFlow};
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::cmp::{CmpItem, ShowOpt};
use yazi_shared::{data::Data, path::{AsPathDyn, PathDyn, PathLike}, strand::{AsStrand, StrandLike}};
use yazi_shared::{data::Data, path::{AsPath, PathDyn}, strand::StrandLike};
use crate::{Actor, Ctx};
@ -30,7 +30,7 @@ impl Actor for Show {
};
cmp.ticket = opt.ticket;
cmp.cands = Self::match_candidates(opt.word.as_path_dyn(), cache);
cmp.cands = Self::match_candidates(opt.word.as_path(), cache);
if cmp.cands.is_empty() {
succ!(render!(mem::replace(&mut cmp.visible, false)));
}
@ -47,16 +47,15 @@ impl Show {
let smart = !word.encoded_bytes().iter().any(|&b| b.is_ascii_uppercase());
let flow = cache.iter().try_fold((Vec::new(), Vec::new()), |(mut exact, mut fuzzy), item| {
let name = item.name.as_strand();
let starts_with =
if smart { name.eq_ignore_ascii_case(word) } else { name.starts_with(word) };
if smart { item.name.eq_ignore_ascii_case(word) } else { item.name.starts_with(word) };
if starts_with {
exact.push(item);
if exact.len() >= LIMIT {
return ControlFlow::Break((exact, fuzzy));
}
} else if fuzzy.len() < LIMIT - exact.len() && name.contains(word) {
} else if fuzzy.len() < LIMIT - exact.len() && item.name.contains(word) {
// Here we don't break the control flow, since we want more exact matching.
fuzzy.push(item)
}

View file

@ -5,7 +5,7 @@ use yazi_fs::{CWD, path::expand_url, provider::{DirReader, FileHolder}};
use yazi_macro::{act, render, succ};
use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt};
use yazi_proxy::CmpProxy;
use yazi_shared::{AnyAsciiChar, data::Data, natsort, path::{AsPath, PathBufDyn, PathLike}, scheme::{SchemeCow, SchemeLike}, strand::StrandBufLike, url::{UrlBuf, UrlCow, UrlLike}};
use yazi_shared::{AnyAsciiChar, data::Data, natsort, path::{PathBufDyn, PathDyn, PathLike}, scheme::{SchemeCow, SchemeLike}, strand::StrandLike, url::{UrlBuf, UrlCow, UrlLike}};
use yazi_vfs::provider;
use crate::{Actor, Ctx};
@ -74,20 +74,17 @@ impl Trigger {
}
let sep = if cfg!(windows) {
AnyAsciiChar::new(&[b'/', b'\\']).unwrap()
AnyAsciiChar::new(b"/\\").unwrap()
} else {
AnyAsciiChar::new(&[b'/']).unwrap()
AnyAsciiChar::new(b"/").unwrap()
};
Some(match path.as_path().rsplit_pred(sep) {
Some(match path.rsplit_pred(sep) {
Some((p, c)) if p.is_empty() => {
let root = PathBufDyn::with(scheme.kind(), MAIN_SEPARATOR_STR).expect("valid root");
let root = PathDyn::with(scheme.kind(), MAIN_SEPARATOR_STR).expect("valid root");
(UrlCow::try_from((scheme, root)).ok()?.into_owned(), c.into())
}
Some((p, c)) => {
let parent = PathBufDyn::with(scheme.kind(), p.as_dyn()).expect("valid parent");
(expand_url(UrlCow::try_from((scheme, parent)).ok()?), c.into())
}
Some((p, c)) => (expand_url(UrlCow::try_from((scheme, p)).ok()?), c.into()),
None => (CWD.load().as_ref().clone(), path.into()),
})
}
@ -95,7 +92,7 @@ impl Trigger {
#[cfg(test)]
mod tests {
use yazi_shared::{path::PathBufLike, url::UrlLike};
use yazi_shared::url::UrlLike;
use super::*;
@ -113,11 +110,19 @@ mod tests {
yazi_fs::init();
compare("", "", "");
compare(" ", "", " ");
compare("/", "/", "");
compare("//", "//", "");
compare("//", "/", "");
compare("///", "/", "");
compare("/foo", "/", "foo");
compare("//foo", "/", "foo");
compare("///foo", "/", "foo");
compare("/foo/", "/foo/", "");
compare("//foo/", "/foo/", "");
compare("/foo/bar", "/foo/", "bar");
compare("///foo/bar", "/foo/", "bar");
}
#[cfg(windows)]

View file

@ -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::{path::PathLike, url::UrlLike};
use yazi_shared::url::UrlLike;
use super::Lives;
use crate::lives::PtrCell;

View file

@ -2,7 +2,7 @@ use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value};
use yazi_binding::{Id, UrlRef, cached_field};
use yazi_shared::{path::PathLike, strand::StrandLike, url::UrlLike};
use yazi_shared::url::UrlLike;
use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected};

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, path::PathLike, url::{AsUrl, UrlBuf, UrlLike}};
use yazi_shared::{Debounce, data::Data, errors::InputError, 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().owned(), file)].into()).emit();
FilesOp::Upserting(p.into(), [(url.urn().into(), 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, path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@ -54,7 +54,7 @@ impl Create {
&& let Some((parent, urn)) = real.pair()
{
ok_or_not_found!(provider::remove_file(&new).await);
FilesOp::Deleting(parent.into(), [urn.owned()].into()).emit();
FilesOp::Deleting(parent.into(), [urn.into()].into()).emit();
provider::create(&new).await?;
} else if let Some(parent) = new.parent() {
provider::create_dir_all(parent).await.ok();
@ -68,7 +68,7 @@ impl Create {
&& let Some((parent, urn)) = real.pair()
{
let file = File::new(&real).await?;
FilesOp::Upserting(parent.into(), [(urn.owned(), file)].into()).emit();
FilesOp::Upserting(parent.into(), [(urn.into(), 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, path::PathLike};
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
@ -16,7 +16,7 @@ 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().owned());
let hovered = cx.hovered().map(|f| f.urn().into());
cx.current_mut().files.set_filter(filter);
if cx.hovered().map(|f| f.urn()) != hovered.as_ref().map(Into::into) {

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, path::PathLike};
use yazi_shared::data::Data;
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().owned());
let hovered = cx.hovered().map(|f| f.urn().to_owned());
let apply = |f: &mut Folder| {
if f.stage == FolderStage::Loading {
render!();

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, path::PathLike, strand::StrandLike, url::{UrlBuf, UrlLike}};
use yazi_shared::{Id, data::Data, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@ -73,10 +73,11 @@ impl Rename {
provider::rename(&old, &new).await?;
if let Ok(u) = overwritten
&& u != new
&& let Some((parent, urn)) = u.pair()
{
ok_or_not_found!(provider::rename(&u, &new).await);
FilesOp::Deleting(parent.to_owned(), [urn.owned()].into()).emit();
FilesOp::Deleting(parent.to_owned(), [urn.into()].into()).emit();
}
let new = provider::casefold(&new).await?;
@ -84,10 +85,10 @@ impl Rename {
let file = File::new(&new).await?;
if new_p == old_p {
FilesOp::Upserting(old_p.into(), [(old_n.owned(), file)].into()).emit();
FilesOp::Upserting(old_p.into(), [(old_n.into(), file)].into()).emit();
} else {
FilesOp::Deleting(old_p.into(), [old_n.owned()].into()).emit();
FilesOp::Upserting(new_p.into(), [(new_n.owned(), 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

@ -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, path::PathLike, url::UrlLike};
use yazi_shared::{data::Data, 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.owned()))?;
act!(mgr:hover, cx, Some(child.into()))?;
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, path::PathLike};
use yazi_shared::data::Data;
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().owned());
let hovered = cx.hovered().map(|f| f.urn().to_owned());
let apply = |f: &mut Folder| {
if f.stage == FolderStage::Loading {
render!();

View file

@ -17,8 +17,7 @@ impl Actor for UpdateFiles {
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let revision = cx.current().files.revision;
let linked: Vec<_> =
LINKED.read().from_dir(opt.op.cwd()).filter_map(|u| opt.op.chdir(u).ok()).collect();
let linked: Vec<_> = LINKED.read().from_dir(opt.op.cwd()).map(|u| opt.op.chdir(u)).collect();
for op in [opt.op].into_iter().chain(linked) {
cx.mgr.yanked.apply_op(&op);

View file

@ -8,6 +8,9 @@ description = "Yazi Lua bindings"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]

View file

@ -154,7 +154,6 @@ macro_rules! impl_file_fields {
$crate::cached_field!($fields, link_to, |_, me| Ok(me.link_to_url().map($crate::Url::new)));
$crate::cached_field!($fields, name, |lua, me| {
use yazi_shared::strand::StrandLike;
me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose()
});
$crate::cached_field!($fields, cache, |_, me| {

View file

@ -1,7 +1,7 @@
use std::ops::Deref;
use mlua::{ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataMethods, Value};
use yazi_shared::path::{PathBufDyn, PathBufLike};
use yazi_shared::path::{PathBufDyn, PathLike};
pub struct Path(pub PathBufDyn);

View file

@ -49,7 +49,7 @@ impl Runtime {
if let Some(v) = self.blocks.get_mut(&cur.id) {
v.push(f);
} else {
self.blocks.insert(cur.id.to_owned(), vec![f]);
self.blocks.insert(cur.id.clone(), vec![f]);
}
true
}

View file

@ -2,7 +2,7 @@ use std::ops::Deref;
use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
use yazi_fs::{FsHash64, FsHash128};
use yazi_shared::{path::{PathLike, StripPrefixError}, strand::{StrandCow, StrandLike}, url::{AsUrl, UrlCow, UrlLike}};
use yazi_shared::{path::StripPrefixError, strand::StrandCow, url::{AsUrl, UrlCow, UrlLike}};
use crate::{Scheme, cached_field, deprecate};

View file

@ -8,6 +8,9 @@ description = "Yazi configuration file parser"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-codegen = { path = "../yazi-codegen", version = "25.9.15" }
yazi-fs = { path = "../yazi-fs", version = "25.9.15" }

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, path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_shared::{SyncCell, url::{UrlBuf, UrlLike}};
use super::{MgrRatio, MouseEvents};

View file

@ -3,7 +3,7 @@ use std::{fmt::Debug, str::FromStr};
use anyhow::{Result, bail};
use globset::{Candidate, GlobBuilder};
use serde::Deserialize;
use yazi_shared::{path::PathLike, scheme::SchemeKind, url::AsUrl};
use yazi_shared::{scheme::SchemeKind, url::AsUrl};
#[derive(Deserialize)]
#[serde(try_from = "String")]
@ -28,7 +28,6 @@ impl Debug for Pattern {
}
impl Pattern {
// FIXME: simplify conditional compilation
pub fn match_url(&self, url: impl AsUrl, is_dir: bool) -> bool {
let url = url.as_url();
@ -47,9 +46,9 @@ impl Pattern {
#[cfg(windows)]
if self.sep_lit {
use yazi_shared::strand::AsStrandDyn;
use yazi_shared::strand::{AsStrand, StrandLike};
self.inner.is_match_candidate(&Candidate::from_bytes(
url.loc().as_strand_dyn().backslash_to_slash().encoded_bytes(),
url.loc().as_strand().backslash_to_slash().encoded_bytes(),
))
} else {
self.inner.is_match_candidate(&Candidate::from_bytes(url.loc().encoded_bytes()))

View file

@ -33,7 +33,7 @@ pub struct ConfirmCfg {
impl InputCfg {
pub fn cd() -> Self {
Self {
title: YAZI.input.cd_title.to_owned(),
title: YAZI.input.cd_title.clone(),
position: Position::new(YAZI.input.cd_origin, YAZI.input.cd_offset),
completion: true,
..Default::default()
@ -42,7 +42,7 @@ impl InputCfg {
pub fn create(dir: bool) -> Self {
Self {
title: YAZI.input.create_title[dir as usize].to_owned(),
title: YAZI.input.create_title[dir as usize].clone(),
position: Position::new(YAZI.input.create_origin, YAZI.input.create_offset),
..Default::default()
}
@ -50,7 +50,7 @@ impl InputCfg {
pub fn rename() -> Self {
Self {
title: YAZI.input.rename_title.to_owned(),
title: YAZI.input.rename_title.clone(),
position: Position::new(YAZI.input.rename_origin, YAZI.input.rename_offset),
..Default::default()
}
@ -58,7 +58,7 @@ impl InputCfg {
pub fn filter() -> Self {
Self {
title: YAZI.input.filter_title.to_owned(),
title: YAZI.input.filter_title.clone(),
position: Position::new(YAZI.input.filter_origin, YAZI.input.filter_offset),
realtime: true,
..Default::default()
@ -67,7 +67,7 @@ impl InputCfg {
pub fn find(prev: bool) -> Self {
Self {
title: YAZI.input.find_title[prev as usize].to_owned(),
title: YAZI.input.find_title[prev as usize].clone(),
position: Position::new(YAZI.input.find_origin, YAZI.input.find_offset),
realtime: true,
..Default::default()
@ -84,7 +84,7 @@ impl InputCfg {
pub fn shell(block: bool) -> Self {
Self {
title: YAZI.input.shell_title[block as usize].to_owned(),
title: YAZI.input.shell_title[block as usize].clone(),
position: Position::new(YAZI.input.shell_origin, YAZI.input.shell_offset),
..Default::default()
}
@ -138,7 +138,7 @@ impl ConfirmCfg {
pub fn overwrite(url: &UrlBuf) -> Self {
Self::new(
YAZI.confirm.overwrite_title.to_owned(),
YAZI.confirm.overwrite_title.clone(),
YAZI.confirm.overwrite_position(),
Some(Text::raw(&YAZI.confirm.overwrite_body)),
Some(url.into_string_lossy().into()),
@ -183,7 +183,7 @@ impl PickCfg {
pub fn open(items: Vec<String>) -> Self {
let max_height = Self::max_height(items.len());
Self {
title: YAZI.pick.open_title.to_owned(),
title: YAZI.pick.open_title.clone(),
items,
position: Position::new(YAZI.pick.open_origin, Offset {
height: max_height,

View file

@ -5,7 +5,7 @@ use hashbrown::HashMap;
use serde::{Deserialize, Deserializer};
use yazi_codegen::DeserializeOver2;
use yazi_fs::File;
use yazi_shared::{Condition, strand::StrandLike, url::UrlLike};
use yazi_shared::{Condition, url::UrlLike};
use crate::{Color, Icon as I, Pattern, Style};

View file

@ -8,6 +8,9 @@ description = "Yazi core logic"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-adapter = { path = "../yazi-adapter", version = "25.9.15" }
yazi-config = { path = "../yazi-config", version = "25.9.15" }

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use hashbrown::HashMap;
use yazi_fs::{Files, Filter, FilterCase};
use yazi_shared::{path::{AsPathDyn, PathBufDyn, PathLike}, url::UrlBuf};
use yazi_shared::{path::{AsPath, PathBufDyn}, url::UrlBuf};
use crate::tab::Folder;
@ -62,7 +62,7 @@ impl Finder {
continue;
}
self.matched.insert(file.urn().owned(), i);
self.matched.insert(file.urn().into(), i);
if self.matched.len() > 99 {
break;
}
@ -78,9 +78,9 @@ impl Finder {
impl Finder {
pub fn matched_idx<T>(&self, folder: &Folder, urn: T) -> Option<u8>
where
T: AsPathDyn,
T: AsPath,
{
if self.lock == *folder { self.matched.get(&urn.as_path_dyn()).copied() } else { None }
if self.lock == *folder { self.matched.get(&urn.as_path()).copied() } else { None }
}
}

View file

@ -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, path::{PathBufDyn, PathBufLike, PathDyn, PathLike}, url::UrlBuf};
use yazi_shared::{Id, path::{AsPath, PathBufDyn, PathDyn}, url::UrlBuf};
use yazi_widgets::Scrollable;
pub struct Folder {
@ -101,7 +101,7 @@ impl Folder {
self.scroll(step)
};
self.trace = self.hovered().filter(|_| b).map(|h| h.urn().owned()).or(self.trace.take());
self.trace = self.hovered().filter(|_| b).map(|h| h.urn().into()).or(self.trace.take());
b |= self.squeeze_offset();
self.sync_page(false);
@ -121,7 +121,7 @@ impl Folder {
if let Some(u) = urn {
self.hover(u)
} else if let Some(u) = &self.trace {
self.hover(u.clone().borrow())
self.hover(u.clone().as_path())
} else {
self.arrow(0)
}

View file

@ -26,7 +26,7 @@ impl Default for Preference {
sort_translit: YAZI.mgr.sort_translit.get(),
// Display
linemode: YAZI.mgr.linemode.to_owned(),
linemode: YAZI.mgr.linemode.clone(),
show_hidden: YAZI.mgr.show_hidden.get(),
}
}

View file

@ -8,6 +8,9 @@ description = "Yazi data distribution service"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]

View file

@ -8,6 +8,9 @@ description = "Yazi foreign function interface"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }

View file

@ -8,6 +8,9 @@ description = "Yazi file manager"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[profile.release]
codegen-units = 1
lto = true

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, strand::StrandBufLike, url::UrlLike};
use yazi_shared::{data::Data, strand::StrandLike, url::UrlLike};
use crate::app::App;

View file

@ -4,7 +4,7 @@ use ratatui::{buffer::Buffer, layout::Rect, widgets::{Block, BorderType, List, L
use yazi_adapter::Dimension;
use yazi_config::{THEME, popup::{Offset, Position}};
use yazi_core::Core;
use yazi_shared::strand::{AsStrand, StrandLike};
use yazi_shared::strand::StrandLike;
pub(crate) struct Cmp<'a> {
core: &'a Core,
@ -26,7 +26,7 @@ impl Widget for Cmp<'_> {
let icon = if x.is_dir { &THEME.cmp.icon_folder } else { &THEME.cmp.icon_file };
let slash = if x.is_dir { MAIN_SEPARATOR_STR } else { "" };
let mut item = ListItem::new(format!(" {icon} {}{slash}", x.name.as_strand().display()));
let mut item = ListItem::new(format!(" {icon} {}{slash}", x.name.display()));
if i == self.core.cmp.rel_cursor() {
item = item.style(THEME.cmp.active);
} else {

View file

@ -8,6 +8,9 @@ description = "Yazi file system"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-ffi = { path = "../yazi-ffi", version = "25.9.15" }
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }

View file

@ -62,8 +62,6 @@ impl Cha {
let mut kind = ChaKind::DUMMY;
let mode = r#type.map(ChaMode::from_bare).unwrap_or_default();
#[cfg(unix)]
use yazi_shared::path::PathLike;
#[cfg(unix)]
if _url.as_url().urn().is_hidden() {
kind |= ChaKind::HIDDEN;

View file

@ -23,7 +23,6 @@ impl ChaKind {
#[cfg(unix)]
{
use yazi_shared::strand::StrandLike;
if _name.as_strand().starts_with(".") {
me |= Self::HIDDEN;
}

View file

@ -80,7 +80,7 @@ impl From<ChaMode> for std::fs::Permissions {
fn from(value: ChaMode) -> Self {
use std::os::unix::fs::PermissionsExt;
std::fs::Permissions::from_mode(value.bits() as _)
Self::from_mode(value.bits() as _)
}
}

View file

@ -114,9 +114,9 @@ impl Serialize for Error {
}
match self {
Error::Kind(kind) => Shadow::Kind { kind: kind_to_str(*kind) }.serialize(serializer),
Error::Raw(code) => Shadow::Raw { code: *code }.serialize(serializer),
Error::Custom { kind, code, message } => {
Self::Kind(kind) => Shadow::Kind { kind: kind_to_str(*kind) }.serialize(serializer),
Self::Raw(code) => Shadow::Raw { code: *code }.serialize(serializer),
Self::Custom { kind, code, message } => {
Shadow::Dyn { kind: kind_to_str(*kind), code: *code, message }.serialize(serializer)
}
}
@ -138,15 +138,15 @@ impl<'de> Deserialize<'de> for Error {
let shadow = Shadow::deserialize(deserializer)?;
Ok(match shadow {
Shadow::Kind { kind } => Error::Kind(kind_from_str(&kind)),
Shadow::Raw { code } => Error::Raw(code),
Shadow::Kind { kind } => Self::Kind(kind_from_str(&kind)),
Shadow::Raw { code } => Self::Raw(code),
Shadow::Dyn { kind, code, message } => {
if !message.is_empty() {
Error::Custom { kind: kind_from_str(&kind), code, message: message.into() }
Self::Custom { kind: kind_from_str(&kind), code, message: message.into() }
} else if let Some(code) = code {
Error::Raw(code)
Self::Raw(code)
} else {
Error::Kind(kind_from_str(&kind))
Self::Kind(kind_from_str(&kind))
}
}
})

View file

@ -1,7 +1,6 @@
use std::{hash::{Hash, Hasher}, ops::Deref, path::Path};
use anyhow::Result;
use yazi_shared::{loc::Loc, path::{AsPathDyn, PathBufDyn, PathDyn}, strand::Strand, url::{Url, UrlBuf, UrlLike}};
use yazi_shared::{loc::Loc, path::{PathBufDyn, PathDyn, PathLike}, strand::Strand, url::{Url, UrlBuf, UrlLike}};
use crate::cha::{Cha, ChaType};
@ -27,15 +26,15 @@ impl File {
}
#[inline]
pub fn chdir(&self, wd: &Path) -> Result<Self> {
Ok(Self { url: self.url.rebase(wd)?, cha: self.cha, link_to: self.link_to.clone() })
pub fn chdir(&self, wd: &Path) -> Self {
Self { url: self.url.rebase(wd), cha: self.cha, link_to: self.link_to.clone() }
}
}
impl File {
// --- Url
#[inline]
pub fn url_owned(&self) -> UrlBuf { self.url.to_owned() }
pub fn url_owned(&self) -> UrlBuf { self.url.clone() }
#[inline]
pub fn uri(&self) -> PathDyn<'_> { self.url.uri() }
@ -50,7 +49,7 @@ impl File {
pub fn stem(&self) -> Option<Strand<'_>> { self.url.stem() }
pub fn link_to_url(&self) -> Option<Url<'_>> {
let to = self.link_to.as_ref()?.as_path_dyn();
let to = self.link_to.as_ref()?;
let kind = self.url.kind();
Some(match &self.url {
UrlBuf::Regular(_) => Url::Regular(Loc::bare(to.as_os().ok()?)),

View file

@ -1,7 +1,7 @@
use std::{mem, ops::{Deref, DerefMut, Not}};
use hashbrown::{HashMap, HashSet};
use yazi_shared::{Id, path::{PathBufDyn, PathDyn, PathLike}};
use yazi_shared::{Id, path::{PathBufDyn, PathDyn}};
use super::{FilesSorter, Filter};
use crate::{FILES_TICKET, File, SortBy};
@ -97,7 +97,7 @@ impl Files {
macro_rules! go {
($dist:expr, $src:expr, $inc:literal) => {
let mut todo: HashMap<_, _> = $src.into_iter().map(|f| (f.urn().owned(), f)).collect();
let mut todo: HashMap<_, _> = $src.into_iter().map(|f| (f.urn().to_owned(), f)).collect();
for f in &$dist {
if todo.remove(&f.urn()).is_some() && todo.is_empty() {
break;
@ -121,19 +121,17 @@ impl Files {
#[cfg(unix)]
pub fn update_deleting(&mut self, urns: HashSet<PathBufDyn>) -> Vec<usize> {
use yazi_shared::path::PathBufLike;
use yazi_shared::path::PathLike;
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.borrow().is_hidden()) || !filter.matches(u))
urns.into_iter().partition(|u| (!self.show_hidden && u.is_hidden()) || !filter.matches(u))
} else if self.show_hidden {
(HashSet::new(), urns)
} else {
urns.into_iter().partition(|u| u.borrow().is_hidden())
urns.into_iter().partition(|u| u.is_hidden())
};
let mut deleted = Vec::with_capacity(items.len());
@ -229,7 +227,7 @@ impl Files {
}
self.update_deleting(
files.iter().filter(|&(u, f)| u != f.urn()).map(|(_, f)| f.urn().owned()).collect(),
files.iter().filter(|&(u, f)| u != f.urn()).map(|(_, f)| f.urn().into()).collect(),
);
let (hidden, items) = self.update_updating(files);

View file

@ -2,7 +2,7 @@ use std::{fmt::Display, ops::Range};
use anyhow::Result;
use regex::bytes::{Regex, RegexBuilder};
use yazi_shared::{event::Cmd, strand::{AsStrand, StrandLike}};
use yazi_shared::{event::Cmd, strand::AsStrand};
pub struct Filter {
raw: String,

View file

@ -1,9 +1,8 @@
use std::path::Path;
use anyhow::Result;
use hashbrown::{HashMap, HashSet};
use yazi_macro::relay;
use yazi_shared::{Id, Ids, path::{PathBufDyn, PathLike}, url::{UrlBuf, UrlLike}};
use yazi_shared::{Id, Ids, path::PathBufDyn, url::{UrlBuf, UrlLike}};
use super::File;
use crate::{cha::Cha, error::Error};
@ -58,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().owned(), n);
parents.entry_ref(&o_p).or_default().1.insert(o.urn().into(), n);
} else {
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);
parents.entry_ref(&o_p).or_default().0.insert(o.urn().into());
parents.entry_ref(&n_p).or_default().1.insert(n.urn().into(), n);
}
}
for (p, (o, n)) in parents {
@ -99,21 +98,16 @@ impl FilesOp {
}
}
pub fn chdir(&self, wd: &Path) -> Result<Self> {
pub fn chdir(&self, wd: &Path) -> Self {
macro_rules! files {
($files:expr) => {{ $files.iter().map(|file| file.chdir(wd)).collect::<Result<_>>()? }};
($files:expr) => {{ $files.iter().map(|file| file.chdir(wd)).collect() }};
}
macro_rules! map {
($map:expr) => {{
$map
.iter()
.map(|(urn, file)| file.chdir(wd).map(|file| (urn.clone(), file)))
.collect::<Result<_>>()?
}};
($map:expr) => {{ $map.iter().map(|(urn, file)| (urn.clone(), file.chdir(wd))).collect() }};
}
let w = UrlBuf::from(wd);
Ok(match self {
match self {
Self::Full(_, files, cha) => Self::Full(w, files!(files), *cha),
Self::Part(_, files, ticket) => Self::Part(w, files!(files), *ticket),
Self::Done(_, cha, ticket) => Self::Done(w, *cha, *ticket),
@ -124,7 +118,7 @@ impl FilesOp {
Self::Deleting(_, urns) => Self::Deleting(w, urns.clone()),
Self::Updating(_, map) => Self::Updating(w, map!(map)),
Self::Upserting(_, map) => Self::Upserting(w, map!(map)),
})
}
}
pub fn diff_recoverable(&self, contains: impl Fn(&UrlBuf) -> bool) -> (Vec<UrlBuf>, Vec<UrlBuf>) {

View file

@ -1,6 +1,6 @@
use std::path::PathBuf;
use yazi_shared::{loc::LocBuf, path::{PathDyn, PathLike}, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
use yazi_shared::{loc::LocBuf, path::PathDyn, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
pub fn clean_url<'a>(url: impl Into<UrlCow<'a>>) -> UrlBuf {
let cow: UrlCow = url.into();

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, path::PathBuf};
use yazi_shared::{FromWtf8Vec, loc::LocBuf, path::{AsPath, PathBufDyn, PathCow, PathDyn, PathLike}, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
use yazi_shared::{FromWtf8Vec, loc::LocBuf, path::{PathBufDyn, PathCow, PathDyn, PathLike}, pool::InternStr, url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}};
use crate::{CWD, path::clean_url};
@ -16,18 +16,13 @@ fn expand_url_impl<'a>(url: Url<'a>) -> UrlCow<'a> {
let n_rest = expand_variables(o_rest);
let n_urn = expand_variables(o_urn);
let rest_diff =
n_rest.as_path().components().count() as isize - o_rest.components().count() as isize;
let urn_diff =
n_urn.as_path().components().count() as isize - o_urn.components().count() as isize;
let rest_diff = n_rest.components().count() as isize - o_rest.components().count() as isize;
let urn_diff = n_urn.components().count() as isize - o_urn.components().count() as isize;
let uri_count = url.uri().components().count() as isize;
let urn_count = url.urn().components().count() as isize;
let mut path = PathBufDyn::with_capacity(
url.kind(),
n_base.as_path().len() + n_rest.as_path().len() + n_urn.as_path().len(),
);
let mut path = PathBufDyn::with_capacity(url.kind(), n_base.len() + n_rest.len() + n_urn.len());
path.try_extend([n_base, n_rest, n_urn]).expect("extend original parts should not fail");
let loc = LocBuf::<PathBuf>::with(
@ -77,7 +72,7 @@ pub fn absolute_url<'a>(url: impl Into<UrlCow<'a>>) -> UrlCow<'a> { absolute_url
fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> {
if url.kind().is_virtual() {
return url.into();
return url;
}
let path = url.loc().as_os().expect("must be a local path");

View file

@ -1,6 +1,6 @@
use std::{io, path::{Path, PathBuf}, sync::Arc};
use yazi_shared::{path::{AsPathDyn, PathBufDyn}, scheme::SchemeKind, url::{Url, UrlBuf, UrlCow}};
use yazi_shared::{path::{AsPath, PathBufDyn}, scheme::SchemeKind, url::{Url, UrlBuf, UrlCow}};
use crate::{cha::Cha, path::absolute_url, provider::{Attrs, Provider}};
@ -31,9 +31,9 @@ impl<'a> Provider for Local<'a> {
#[inline]
async fn copy<P>(&self, to: P, attrs: Attrs) -> io::Result<u64>
where
P: AsPathDyn,
P: AsPath,
{
let to = to.as_path_dyn().to_os_owned()?;
let to = to.as_path().to_os_owned()?;
let from = self.path.to_owned();
Self::copy_impl(from, to, attrs).await
}
@ -47,9 +47,9 @@ impl<'a> Provider for Local<'a> {
#[inline]
async fn hard_link<P>(&self, to: P) -> io::Result<()>
where
P: AsPathDyn,
P: AsPath,
{
let to = to.as_path_dyn().as_os()?;
let to = to.as_path().as_os()?;
tokio::fs::hard_link(self.path, to).await
}
@ -62,7 +62,7 @@ impl<'a> Provider for Local<'a> {
#[inline]
async fn new<'b>(url: Url<'b>) -> io::Result<Self::Me<'b>> {
match url {
Url::Regular(loc) | Url::Search { loc, .. } => Ok(Self::Me { url, path: loc.as_path() }),
Url::Regular(loc) | Url::Search { loc, .. } => Ok(Self::Me { url, path: loc.as_inner() }),
Url::Archive { .. } | Url::Sftp { .. } => {
Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Not a local URL: {url:?}")))
}
@ -101,9 +101,9 @@ impl<'a> Provider for Local<'a> {
#[inline]
async fn rename<P>(&self, to: P) -> io::Result<()>
where
P: AsPathDyn,
P: AsPath,
{
let to = to.as_path_dyn().as_os()?;
let to = to.as_path().as_os()?;
tokio::fs::rename(self.path, to).await
}
@ -111,12 +111,12 @@ impl<'a> Provider for Local<'a> {
#[inline]
async fn symlink<P, F>(&self, original: P, _is_dir: F) -> io::Result<()>
where
P: AsPathDyn,
P: AsPath,
F: AsyncFnOnce() -> io::Result<bool>,
{
#[cfg(unix)]
{
let original = original.as_path_dyn().as_os()?;
let original = original.as_path().as_os()?;
tokio::fs::symlink(original, self.path).await
}
#[cfg(windows)]
@ -130,9 +130,9 @@ impl<'a> Provider for Local<'a> {
#[inline]
async fn symlink_dir<P>(&self, original: P) -> io::Result<()>
where
P: AsPathDyn,
P: AsPath,
{
let original = original.as_path_dyn().as_os()?;
let original = original.as_path().as_os()?;
#[cfg(unix)]
{
@ -147,9 +147,9 @@ impl<'a> Provider for Local<'a> {
#[inline]
async fn symlink_file<P>(&self, original: P) -> io::Result<()>
where
P: AsPathDyn,
P: AsPath,
{
let original = original.as_path_dyn().as_os()?;
let original = original.as_path().as_os()?;
#[cfg(unix)]
{

View file

@ -2,7 +2,7 @@ use std::io;
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
use yazi_macro::ok_or_not_found;
use yazi_shared::{path::{AsPathDyn, PathBufDyn, PathLike}, strand::StrandCow, url::{AsUrl, Url, UrlBuf}};
use yazi_shared::{path::{AsPath, PathBufDyn}, strand::StrandCow, url::{AsUrl, Url, UrlBuf}};
use crate::{cha::{Cha, ChaType}, provider::Attrs};
@ -21,7 +21,7 @@ pub trait Provider: Sized {
fn copy<P>(&self, to: P, attrs: Attrs) -> impl Future<Output = io::Result<u64>>
where
P: AsPathDyn;
P: AsPath;
fn create(&self) -> impl Future<Output = io::Result<Self::File>> {
async move { self.gate().write(true).create(true).truncate(true).open(self.url()).await }
@ -69,7 +69,7 @@ pub trait Provider: Sized {
fn hard_link<P>(&self, to: P) -> impl Future<Output = io::Result<()>>
where
P: AsPathDyn;
P: AsPath;
fn metadata(&self) -> impl Future<Output = io::Result<Cha>>;
@ -144,23 +144,23 @@ pub trait Provider: Sized {
fn rename<P>(&self, to: P) -> impl Future<Output = io::Result<()>>
where
P: AsPathDyn;
P: AsPath;
fn symlink<P, F>(&self, original: P, _is_dir: F) -> impl Future<Output = io::Result<()>>
where
P: AsPathDyn,
P: AsPath,
F: AsyncFnOnce() -> io::Result<bool>;
fn symlink_dir<P>(&self, original: P) -> impl Future<Output = io::Result<()>>
where
P: AsPathDyn,
P: AsPath,
{
self.symlink(original, async || Ok(true))
}
fn symlink_file<P>(&self, original: P) -> impl Future<Output = io::Result<()>>
where
P: AsPathDyn,
P: AsPath,
{
self.symlink(original, async || Ok(false))
}

View file

@ -1,7 +1,7 @@
use std::cmp::Ordering;
use hashbrown::HashMap;
use yazi_shared::{LcgRng, natsort, path::{PathBufDyn, PathLike}, strand::StrandLike, translit::Transliterator, url::UrlLike};
use yazi_shared::{LcgRng, natsort, path::PathBufDyn, translit::Transliterator, url::UrlLike};
use crate::{File, SortBy};

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
use yazi_shared::{path::{PathDyn, PathLike}, url::{AsUrl, Url, UrlBuf, UrlCow}};
use yazi_shared::{path::PathDyn, url::{AsUrl, Url, UrlBuf, UrlCow}};
use crate::{FsHash128, FsScheme, path::PercentEncoding};
@ -48,7 +48,7 @@ impl<'a> FsUrl<'a> for Url<'a> {
fn unified_path(self) -> Cow<'a, Path> {
match self {
Self::Regular(loc) | Self::Search { loc, .. } => loc.as_path().into(),
Self::Regular(loc) | Self::Search { loc, .. } => loc.as_inner().into(),
Self::Archive { .. } | Self::Sftp { .. } => {
self.cache().expect("non-local URL should have a cache path").into()
}
@ -63,7 +63,7 @@ impl FsUrl<'_> for UrlBuf {
fn unified_path(self) -> Cow<'static, Path> {
match self {
Self::Regular(loc) | Self::Search { loc, .. } => loc.into_path().into(),
Self::Regular(loc) | Self::Search { loc, .. } => loc.into_inner().into(),
Self::Archive { .. } | Self::Sftp { .. } => {
self.cache().expect("non-local URL should have a cache path").into()
}
@ -78,8 +78,8 @@ impl<'a> FsUrl<'a> for UrlCow<'a> {
fn unified_path(self) -> Cow<'a, Path> {
match self {
Self::Regular(loc) | Self::Search { loc, .. } => loc.into_path().into(),
Self::RegularRef(loc) | Self::SearchRef { loc, .. } => loc.as_path().into(),
Self::Regular(loc) | Self::Search { loc, .. } => loc.into_inner().into(),
Self::RegularRef(loc) | Self::SearchRef { loc, .. } => loc.as_inner().into(),
Self::Archive { .. } | Self::ArchiveRef { .. } | Self::Sftp { .. } | Self::SftpRef { .. } => {
self.cache().expect("non-local URL should have a cache path").into()
}

View file

@ -7,3 +7,6 @@ authors = [ "sxyazi <sxyazi@gmail.com>" ]
description = "Yazi macros"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true

View file

@ -8,18 +8,21 @@ description = "Yazi packing"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[package.metadata.deb]
name = "yazi"
license-file = [ "../LICENSE", "0" ]
depends = "file, ffmpeg, 7zip, jq, poppler-utils, fd-find|fd, ripgrep, fzf, zoxide, imagemagick, xsel|xclip|wl-clipboard"
recommends = "bash-completion"
extended-description-file = "README.md"
section = "utility"
priority = "optional"
assets = [
[lints]
workspace = true
[package.metadata.deb]
name = "yazi"
license-file = [ "../LICENSE", "0" ]
depends = "file, ffmpeg, 7zip, jq, poppler-utils, fd-find|fd, ripgrep, fzf, zoxide, imagemagick, xsel|xclip|wl-clipboard"
recommends = "bash-completion"
extended-description-file = "README.md"
section = "utility"
priority = "optional"
assets = [
[ "target/release/ya", "usr/bin/", "755" ],
[ "target/release/yazi", "usr/bin/", "755" ],
[ "../README.md", "usr/share/doc/yazi/README", "644" ],
[ "../yazi-cli/completions/ya.bash", "usr/share/bash-completion/completions/ya", "644" ],
[ "../yazi-boot/completions/yazi.bash", "usr/share/bash-completion/completions/yazi", "644" ],
]
]

View file

@ -8,6 +8,9 @@ description = "Yazi command parser"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]

View file

@ -2,7 +2,7 @@ use std::path::MAIN_SEPARATOR_STR;
use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_shared::{Id, event::CmdCow, path::PathBufDyn, strand::{StrandBuf, StrandBufLike}, url::UrlBuf};
use yazi_shared::{Id, event::CmdCow, path::PathBufDyn, strand::{StrandBuf, StrandLike}, url::UrlBuf};
#[derive(Debug)]
pub struct ShowOpt {

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, str::FromStr};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use serde::Deserialize;
use yazi_shared::{SStr, event::CmdCow, strand::{AsStrandDyn, StrandLike}};
use yazi_shared::{SStr, event::CmdCow, strand::AsStrand};
#[derive(Debug)]
pub struct CopyOpt {
@ -49,16 +49,16 @@ impl FromStr for CopySeparator {
impl CopySeparator {
pub fn transform<T>(self, s: &T) -> Cow<'_, [u8]>
where
T: ?Sized + AsStrandDyn,
T: ?Sized + AsStrand,
{
#[cfg(windows)]
if self == Self::Unix {
use yazi_shared::strand::{StrandBufLike, StrandCow};
return match s.as_strand_dyn().backslash_to_slash() {
use yazi_shared::strand::StrandCow;
return match s.as_strand().backslash_to_slash() {
StrandCow::Borrowed(s) => s.encoded_bytes().into(),
StrandCow::Owned(s) => s.into_encoded_bytes().into(),
};
}
Cow::Borrowed(s.as_strand_dyn().encoded_bytes())
Cow::Borrowed(s.as_strand().encoded_bytes())
}
}

View file

@ -8,6 +8,9 @@ description = "Yazi plugin system"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]

View file

@ -8,6 +8,9 @@ description = "Yazi event proxy"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-config = { path = "../yazi-config", version = "25.9.15" }
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }

View file

@ -8,6 +8,9 @@ description = "Yazi task scheduler"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-config = { path = "../yazi-config", version = "25.9.15" }
yazi-dds = { path = "../yazi-dds", version = "25.9.15" }

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, path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_shared::{event::CmdCow, 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().owned(), s))),
HashMap::from_iter(buf.into_iter().map(|(u, s)| (u.urn().into(), s))),
)
.emit();
});

View file

@ -8,6 +8,9 @@ description = "Yazi SFTP client"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
bitflags = { workspace = true }
parking_lot = { workspace = true }

View file

@ -17,7 +17,7 @@ impl<'a> From<&'a str> for ByteStr<'a> {
fn from(value: &'a str) -> Self { ByteStr(Cow::Borrowed(value.as_bytes())) }
}
impl<'a> From<&'a ByteStr<'a>> for ByteStr<'a> {
impl<'a> From<&'a Self> for ByteStr<'a> {
fn from(value: &'a ByteStr) -> Self { ByteStr(Cow::Borrowed(&value.0)) }
}
@ -67,7 +67,7 @@ impl<'a> ByteStr<'a> {
}
}
pub fn join(&self, other: impl Into<ByteStr<'a>>) -> PathBuf {
pub fn join(&self, other: impl Into<Self>) -> PathBuf {
let other = other.into();
match self.to_path() {
Cow::Borrowed(p) => p.join(other.to_path()),

View file

@ -9,6 +9,9 @@ homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
rust-version = "1.91.0"
[lints]
workspace = true
[dependencies]
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }

View file

@ -15,8 +15,8 @@ pub enum Data {
Integer(i64),
Number(f64),
String(SStr),
List(Vec<Data>),
Dict(HashMap<DataKey, Data>),
List(Vec<Self>),
Dict(HashMap<DataKey, Self>),
Id(Id),
#[serde(skip_deserializing)]
Url(UrlBuf),

142
yazi-shared/src/loc/able.rs Normal file
View file

@ -0,0 +1,142 @@
use std::{ffi::{OsStr, OsString}, fmt::Debug, hash::Hash};
use crate::{path::{AsPath, AsPathView}, strand::AsStrandView};
// --- LocAble
pub trait LocAble<'p>
where
Self: Copy + AsStrandView<'p, Self::Strand<'p>>,
{
type Strand<'a>: StrandAble<'a> + StrandAbleImpl<'a>;
type Owned: LocBufAble + LocBufAbleImpl + Into<Self::Owned>;
type Components<'a>: Clone + DoubleEndedIterator + AsStrandView<'a, Self::Strand<'a>>;
}
impl<'p> LocAble<'p> for &'p std::path::Path {
type Components<'a> = std::path::Components<'a>;
type Owned = std::path::PathBuf;
type Strand<'a> = &'a OsStr;
}
// --- LocBufAble
pub trait LocBufAble
where
Self: 'static + AsPath + Default,
{
type Strand<'a>: StrandAble<'a>;
type Borrowed<'a>: LocAble<'a>
+ LocAbleImpl<'a>
+ AsPathView<'a, Self::Borrowed<'a>>
+ Debug
+ Hash;
}
impl LocBufAble for std::path::PathBuf {
type Borrowed<'a> = &'a std::path::Path;
type Strand<'a> = &'a OsStr;
}
// --- StrandAble
pub trait StrandAble<'a>: Copy {}
impl<'a> StrandAble<'a> for &'a OsStr {}
// --- LocAbleImpl
pub(super) trait LocAbleImpl<'p>: LocAble<'p> {
fn as_encoded_bytes(self) -> &'p [u8];
fn components(self) -> Self::Components<'p>;
fn empty() -> Self;
fn file_name(self) -> Option<Self::Strand<'p>>;
unsafe fn from_encoded_bytes_unchecked(bytes: &'p [u8]) -> Self;
fn join<'a, T>(self, path: T) -> Self::Owned
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn len(self) -> usize { self.as_encoded_bytes().len() }
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn to_path_buf(self) -> Self::Owned;
}
impl<'p> LocAbleImpl<'p> for &'p std::path::Path {
fn as_encoded_bytes(self) -> &'p [u8] { self.as_os_str().as_encoded_bytes() }
fn components(self) -> Self::Components<'p> { self.components() }
fn empty() -> Self { std::path::Path::new("") }
fn file_name(self) -> Option<Self::Strand<'p>> { self.file_name() }
unsafe fn from_encoded_bytes_unchecked(bytes: &'p [u8]) -> Self {
std::path::Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) })
}
fn join<'a, T>(self, path: T) -> Self::Owned
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
self.join(path.as_strand_view())
}
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
self.strip_prefix(base.as_strand_view()).ok()
}
fn to_path_buf(self) -> Self::Owned { self.to_path_buf() }
}
// --- LocBufAbleImpl
pub(super) trait LocBufAbleImpl: LocBufAble {
fn as_encoded_bytes(&self) -> &[u8] { self.borrow().as_encoded_bytes() }
fn borrow(&self) -> Self::Borrowed<'_>;
unsafe fn from_encoded_bytes_unchecked(bytes: Vec<u8>) -> Self;
fn into_encoded_bytes(self) -> Vec<u8>;
fn len(&self) -> usize { self.borrow().len() }
fn set_file_name<'a, T>(&mut self, name: T)
where
T: AsStrandView<'a, Self::Strand<'a>>;
}
impl LocBufAbleImpl for std::path::PathBuf {
fn borrow(&self) -> Self::Borrowed<'_> { self.as_path() }
unsafe fn from_encoded_bytes_unchecked(bytes: Vec<u8>) -> Self {
Self::from(unsafe { OsString::from_encoded_bytes_unchecked(bytes) })
}
fn into_encoded_bytes(self) -> Vec<u8> { self.into_os_string().into_encoded_bytes() }
fn set_file_name<'a, T>(&mut self, name: T)
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
self.set_file_name(name.as_strand_view())
}
}
// --- StrandAbleImpl
pub(super) trait StrandAbleImpl<'a>: StrandAble<'a> {
fn as_encoded_bytes(self) -> &'a [u8];
fn len(self) -> usize { self.as_encoded_bytes().len() }
}
impl<'a> StrandAbleImpl<'a> for &'a OsStr {
fn as_encoded_bytes(self) -> &'a [u8] { self.as_encoded_bytes() }
}

View file

@ -1,11 +1,11 @@
use std::{cmp, ffi::OsStr, fmt::{self, Debug, Formatter}, hash::{Hash, Hasher}, marker::PhantomData, ops::Deref, path::PathBuf};
use std::{cmp, ffi::OsStr, fmt::{self, Debug, Formatter}, hash::{Hash, Hasher}, marker::PhantomData, mem, ops::Deref, path::PathBuf};
use anyhow::Result;
use crate::{loc::Loc, path::{AsPathDyn, AsPathView, PathBufLike, PathBufUnsafeExt, PathDyn, PathLike, PathUnsafeExt, SetNameError}, scheme::SchemeKind, strand::AsStrandView};
use crate::{loc::{Loc, LocAble, LocAbleImpl, LocBufAble, LocBufAbleImpl}, path::{AsPath, AsPathView, PathDyn, SetNameError}, scheme::SchemeKind, strand::AsStrandView};
#[derive(Clone, Default, Eq)]
pub struct LocBuf<P: PathBufLike = PathBuf> {
#[derive(Clone, Default, Eq, PartialEq)]
pub struct LocBuf<P = PathBuf> {
pub(super) inner: P,
pub(super) uri: usize,
pub(super) urn: usize,
@ -13,7 +13,7 @@ pub struct LocBuf<P: PathBufLike = PathBuf> {
impl<P> Deref for LocBuf<P>
where
P: PathBufLike,
P: LocBufAble,
{
type Target = P;
@ -25,37 +25,30 @@ 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>
impl<T> AsPath for LocBuf<T>
where
T: PathBufLike + AsPathDyn,
T: LocBufAble + AsPath,
{
fn as_path_dyn(&self) -> PathDyn<'_> { self.inner.as_path_dyn() }
fn as_path(&self) -> PathDyn<'_> { self.inner.as_path() }
}
impl<T> AsPathDyn for &LocBuf<T>
impl<T> AsPath for &LocBuf<T>
where
T: PathBufLike + AsPathDyn,
T: LocBufAble + AsPath,
{
fn as_path_dyn(&self) -> PathDyn<'_> { self.inner.as_path_dyn() }
}
impl<P> PartialEq for LocBuf<P>
where
P: PathBufLike + PartialEq,
{
fn eq(&self, other: &Self) -> bool { self.inner == other.inner }
fn as_path(&self) -> PathDyn<'_> { self.inner.as_path() }
}
impl<P> Ord for LocBuf<P>
where
P: PathBufLike + Ord,
P: LocBufAble + Ord,
{
fn cmp(&self, other: &Self) -> cmp::Ordering { self.inner.cmp(&other.inner) }
}
impl<P> PartialOrd for LocBuf<P>
where
P: PathBufLike + PartialOrd,
P: LocBufAble + PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
@ -65,8 +58,7 @@ where
// --- Hash
impl<P> Hash for LocBuf<P>
where
P: PathBufLike + PathBufUnsafeExt,
for<'a> P::Borrowed<'a>: PathUnsafeExt<'a>,
P: LocBufAble + LocBufAbleImpl,
for<'a> &'a P: AsPathView<'a, P::Borrowed<'a>>,
{
fn hash<H: Hasher>(&self, state: &mut H) { self.as_loc().hash(state) }
@ -74,8 +66,7 @@ where
impl<P> Debug for LocBuf<P>
where
P: PathBufLike + PathBufUnsafeExt + Debug,
for<'a> P::Borrowed<'a>: PathUnsafeExt<'a>,
P: LocBufAble + LocBufAbleImpl + Debug,
for<'a> &'a P: AsPathView<'a, P::Borrowed<'a>>,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
@ -89,8 +80,7 @@ where
impl<P> From<P> for LocBuf<P>
where
P: PathBufLike + PathBufUnsafeExt,
for<'a> P::Borrowed<'a>: PathUnsafeExt<'a>,
P: LocBufAble + LocBufAbleImpl,
for<'a> &'a P: AsPathView<'a, P::Borrowed<'a>>,
{
fn from(path: P) -> Self {
@ -99,7 +89,7 @@ where
let mut bytes = path.into_encoded_bytes();
bytes.truncate(len);
Self { inner: unsafe { P::from_encoded_bytes(bytes) }, uri, urn }
Self { inner: unsafe { P::from_encoded_bytes_unchecked(bytes) }, uri, urn }
}
}
@ -109,29 +99,28 @@ impl<T: ?Sized + AsRef<OsStr>> From<&T> for LocBuf<PathBuf> {
impl<P> LocBuf<P>
where
P: PathBufLike + PathBufUnsafeExt,
for<'a> P::Borrowed<'a>: PathUnsafeExt<'a>,
P: LocBufAble + LocBufAbleImpl,
for<'a> &'a P: AsPathView<'a, P::Borrowed<'a>>,
{
pub fn new<'a, S>(path: P, base: S, trail: S) -> Self
where
S: for<'b> AsStrandView<'a, <P::Borrowed<'b> as PathLike<'b>>::Strand<'a>>,
S: for<'b> AsStrandView<'a, <P::Borrowed<'b> as LocAble<'b>>::Strand<'a>>,
{
let loc = Self::from(path);
let Loc { inner, uri, urn, _phantom } = Loc::new(&loc.inner, base, trail);
debug_assert!(inner.encoded_bytes() == loc.inner.encoded_bytes());
debug_assert!(inner.as_encoded_bytes() == loc.inner.as_encoded_bytes());
Self { inner: loc.inner, uri, urn }
}
pub fn with(path: P, uri: usize, urn: usize) -> Result<Self>
where
for<'a> P::Borrowed<'a>: PathLike<'a>,
for<'a> P::Borrowed<'a>: LocAble<'a>,
{
let loc = Self::from(path);
let Loc { inner, uri, urn, _phantom } = Loc::with(&loc.inner, uri, urn)?;
debug_assert!(inner.encoded_bytes() == loc.inner.encoded_bytes());
debug_assert!(inner.as_encoded_bytes() == loc.inner.as_encoded_bytes());
Ok(Self { inner: loc.inner, uri, urn })
}
@ -142,18 +131,18 @@ where
let loc = Self::from(path.into());
let Loc { inner, uri, urn, _phantom } = Loc::zeroed(&loc.inner);
debug_assert!(inner.encoded_bytes() == loc.inner.encoded_bytes());
debug_assert!(inner.as_encoded_bytes() == loc.inner.as_encoded_bytes());
Self { inner: loc.inner, uri, urn }
}
pub fn floated<'a, S>(path: P, base: S) -> Self
where
S: for<'b> AsStrandView<'a, <P::Borrowed<'b> as PathLike<'b>>::Strand<'a>>,
S: for<'b> AsStrandView<'a, <P::Borrowed<'b> as LocAble<'b>>::Strand<'a>>,
{
let loc = Self::from(path);
let Loc { inner, uri, urn, _phantom } = Loc::floated(&loc.inner, base);
debug_assert!(inner.encoded_bytes() == loc.inner.encoded_bytes());
debug_assert!(inner.as_encoded_bytes() == loc.inner.as_encoded_bytes());
Self { inner: loc.inner, uri, urn }
}
@ -161,7 +150,7 @@ where
let loc = Self::from(path);
let Loc { inner, uri, urn, _phantom } = Loc::saturated(&loc.inner, kind);
debug_assert!(inner.encoded_bytes() == loc.inner.encoded_bytes());
debug_assert!(inner.as_encoded_bytes() == loc.inner.as_encoded_bytes());
Self { inner: loc.inner, uri, urn }
}
@ -176,7 +165,7 @@ where
}
#[inline]
pub fn to_path(&self) -> P
pub fn to_inner(&self) -> P
where
P: Clone,
{
@ -184,14 +173,14 @@ where
}
#[inline]
pub fn into_path(self) -> P { self.inner }
pub fn into_inner(self) -> P { self.inner }
pub fn try_set_name<'a, T>(&mut self, name: T) -> Result<(), SetNameError>
where
T: AsStrandView<'a, P::Strand<'a>>,
{
let old = self.inner.len();
self.mutate(|path| path.try_set_name(name))?;
self.mutate(|path| path.set_file_name(name));
let new = self.len();
if new == old {
@ -216,19 +205,19 @@ where
}
#[inline]
pub fn rebase<'a, 'b>(&'a self, base: P::Borrowed<'b>) -> Result<Self>
pub fn rebase<'a, 'b>(&'a self, base: P::Borrowed<'b>) -> Self
where
'a: 'b,
for<'c> <P::Borrowed<'c> as PathLike<'c>>::Owned: Into<Self>,
for<'c> <P::Borrowed<'c> as LocAble<'c>>::Owned: Into<Self>,
{
let mut loc: Self = base.try_join(self.uri())?.into();
let mut loc: Self = base.join(self.uri()).into();
(loc.uri, loc.urn) = (self.uri, self.urn);
Ok(loc)
loc
}
#[inline]
fn mutate<T, F: FnOnce(&mut P) -> T>(&mut self, f: F) -> T {
let mut inner = self.inner.take();
let mut inner = mem::take(&mut self.inner);
let result = f(&mut inner);
self.inner = Self::from(inner).inner;
result
@ -238,8 +227,7 @@ where
// FIXME: macro
impl<P> LocBuf<P>
where
P: PathBufLike + PathBufUnsafeExt,
for<'a> P::Borrowed<'a>: PathUnsafeExt<'a>,
P: LocBufAble + LocBufAbleImpl,
for<'a> &'a P: AsPathView<'a, P::Borrowed<'a>>,
{
#[inline]
@ -259,19 +247,6 @@ where
#[inline]
pub fn has_trail(&self) -> bool { self.as_loc().has_trail() }
#[inline]
pub fn name(&self) -> Option<<P::Borrowed<'_> as PathLike<'_>>::Strand<'_>> {
self.as_loc().name()
}
#[inline]
pub fn stem(&self) -> Option<<P::Borrowed<'_> as PathLike<'_>>::Strand<'_>> {
self.as_loc().stem()
}
#[inline]
pub fn ext(&self) -> Option<<P::Borrowed<'_> as PathLike<'_>>::Strand<'_>> { self.as_loc().ext() }
}
impl LocBuf<PathBuf> {
@ -290,22 +265,22 @@ mod tests {
let loc: LocBuf = Path::new("/").into();
assert_eq!(loc.uri().as_os_str(), OsStr::new("/"));
assert_eq!(loc.urn().as_os_str(), OsStr::new(""));
assert_eq!(loc.name(), None);
assert_eq!(loc.file_name(), None);
assert_eq!(loc.base().as_os_str(), OsStr::new(""));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/"));
let loc: LocBuf = Path::new("/root").into();
assert_eq!(loc.uri().as_os_str(), OsStr::new("root"));
assert_eq!(loc.uri().as_os_str(), OsStr::new("/root"));
assert_eq!(loc.urn().as_os_str(), OsStr::new("root"));
assert_eq!(loc.name().unwrap(), OsStr::new("root"));
assert_eq!(loc.base().as_os_str(), OsStr::new("/"));
assert_eq!(loc.file_name().unwrap(), OsStr::new("root"));
assert_eq!(loc.base().as_os_str(), OsStr::new(""));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/"));
let loc: LocBuf = Path::new("/root/code/foo/").into();
assert_eq!(loc.uri().as_os_str(), OsStr::new("foo"));
assert_eq!(loc.uri().as_os_str(), OsStr::new("/root/code/foo"));
assert_eq!(loc.urn().as_os_str(), OsStr::new("foo"));
assert_eq!(loc.name().unwrap(), OsStr::new("foo"));
assert_eq!(loc.base().as_os_str(), OsStr::new("/root/code/"));
assert_eq!(loc.file_name().unwrap(), OsStr::new("foo"));
assert_eq!(loc.base().as_os_str(), OsStr::new(""));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/root/code/"));
}
@ -314,42 +289,42 @@ mod tests {
let loc = LocBuf::<PathBuf>::with("/".into(), 0, 0)?;
assert_eq!(loc.uri().as_os_str(), OsStr::new(""));
assert_eq!(loc.urn().as_os_str(), OsStr::new(""));
assert_eq!(loc.name(), None);
assert_eq!(loc.file_name(), None);
assert_eq!(loc.base().as_os_str(), OsStr::new("/"));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/"));
let loc = LocBuf::<PathBuf>::with("/root/code/".into(), 1, 1)?;
assert_eq!(loc.uri().as_os_str(), OsStr::new("code"));
assert_eq!(loc.urn().as_os_str(), OsStr::new("code"));
assert_eq!(loc.name().unwrap(), OsStr::new("code"));
assert_eq!(loc.file_name().unwrap(), OsStr::new("code"));
assert_eq!(loc.base().as_os_str(), OsStr::new("/root/"));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/root/"));
let loc = LocBuf::<PathBuf>::with("/root/code/foo//".into(), 2, 1)?;
assert_eq!(loc.uri().as_os_str(), OsStr::new("code/foo"));
assert_eq!(loc.urn().as_os_str(), OsStr::new("foo"));
assert_eq!(loc.name().unwrap(), OsStr::new("foo"));
assert_eq!(loc.file_name().unwrap(), OsStr::new("foo"));
assert_eq!(loc.base().as_os_str(), OsStr::new("/root/"));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/root/code/"));
let loc = LocBuf::<PathBuf>::with("/root/code/foo//".into(), 2, 2)?;
assert_eq!(loc.uri().as_os_str(), OsStr::new("code/foo"));
assert_eq!(loc.urn().as_os_str(), OsStr::new("code/foo"));
assert_eq!(loc.name().unwrap(), OsStr::new("foo"));
assert_eq!(loc.file_name().unwrap(), OsStr::new("foo"));
assert_eq!(loc.base().as_os_str(), OsStr::new("/root/"));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/root/"));
let loc = LocBuf::<PathBuf>::with("/root/code/foo//bar/".into(), 2, 2)?;
assert_eq!(loc.uri().as_os_str(), OsStr::new("foo//bar"));
assert_eq!(loc.urn().as_os_str(), OsStr::new("foo//bar"));
assert_eq!(loc.name().unwrap(), OsStr::new("bar"));
assert_eq!(loc.file_name().unwrap(), OsStr::new("bar"));
assert_eq!(loc.base().as_os_str(), OsStr::new("/root/code/"));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/root/code/"));
let loc = LocBuf::<PathBuf>::with("/root/code/foo//bar/".into(), 3, 2)?;
assert_eq!(loc.uri().as_os_str(), OsStr::new("code/foo//bar"));
assert_eq!(loc.urn().as_os_str(), OsStr::new("foo//bar"));
assert_eq!(loc.name().unwrap(), OsStr::new("bar"));
assert_eq!(loc.file_name().unwrap(), OsStr::new("bar"));
assert_eq!(loc.base().as_os_str(), OsStr::new("/root/"));
assert_eq!(loc.trail().as_os_str(), OsStr::new("/root/code/"));
Ok(())

View file

@ -2,7 +2,8 @@ use std::{hash::{Hash, Hasher}, marker::PhantomData, ops::Deref, path::Path};
use anyhow::{Result, bail};
use crate::{loc::LocBuf, path::{AsPathDyn, AsPathView, PathBufLike, PathDyn, PathLike, PathUnsafeExt}, scheme::SchemeKind, strand::{AsStrandView, StrandLike}};
use super::LocAbleImpl;
use crate::{loc::{LocAble, LocBuf, LocBufAble, StrandAbleImpl}, path::{AsPath, AsPathView, PathDyn}, scheme::SchemeKind, strand::AsStrandView};
#[derive(Clone, Copy, Debug)]
pub struct Loc<'p, P = &'p Path> {
@ -14,25 +15,25 @@ pub struct Loc<'p, P = &'p Path> {
impl<'p, P> Default for Loc<'p, P>
where
P: PathLike<'p>,
P: LocAble<'p> + LocAbleImpl<'p>,
{
fn default() -> Self { Self { inner: P::default(), uri: 0, urn: 0, _phantom: PhantomData } }
fn default() -> Self { Self { inner: P::empty(), uri: 0, urn: 0, _phantom: PhantomData } }
}
impl<'p, P> Deref for Loc<'p, P>
where
P: PathLike<'p>,
P: LocAble<'p>,
{
type Target = P;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl<'p, P> AsPathDyn for Loc<'p, P>
impl<'p, P> AsPath for Loc<'p, P>
where
P: PathLike<'p> + AsPathDyn,
P: LocAble<'p> + AsPath,
{
fn as_path_dyn(&self) -> PathDyn<'_> { self.inner.as_path_dyn() }
fn as_path(&self) -> PathDyn<'_> { self.inner.as_path() }
}
// FIXME: remove
@ -43,34 +44,34 @@ impl AsRef<std::path::Path> for Loc<'_, &std::path::Path> {
// --- Hash
impl<'p, P> Hash for Loc<'p, P>
where
P: PathLike<'p> + Hash,
P: LocAble<'p> + Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) { self.inner.hash(state) }
}
impl<'p, P> From<Loc<'p, P>> for LocBuf<<P as PathLike<'p>>::Owned>
impl<'p, P> From<Loc<'p, P>> for LocBuf<<P as LocAble<'p>>::Owned>
where
P: PathLike<'p>,
<P as PathLike<'p>>::Owned: PathBufLike,
P: LocAble<'p> + LocAbleImpl<'p>,
<P as LocAble<'p>>::Owned: LocBufAble,
{
fn from(value: Loc<'p, P>) -> Self {
Self { inner: value.inner.owned(), uri: value.uri, urn: value.urn }
Self { inner: value.inner.to_path_buf(), uri: value.uri, urn: value.urn }
}
}
// --- Eq
impl<'p, P> PartialEq for Loc<'p, P>
where
P: PathLike<'p> + PartialEq,
P: LocAble<'p> + PartialEq,
{
fn eq(&self, other: &Self) -> bool { self.inner == other.inner }
}
impl<'p, P> Eq for Loc<'p, P> where P: PathLike<'p> + Eq {}
impl<'p, P> Eq for Loc<'p, P> where P: LocAble<'p> + Eq {}
impl<'p, P> Loc<'p, P>
where
P: PathLike<'p> + PathUnsafeExt<'p>,
P: LocAble<'p> + LocAbleImpl<'p>,
{
pub fn new<'a, T, S>(path: T, base: S, trail: S) -> Self
where
@ -78,8 +79,8 @@ where
S: AsStrandView<'a, P::Strand<'a>>,
{
let mut loc = Self::bare(path);
loc.uri = loc.inner.try_strip_prefix(base).expect("Loc must start with the given base").len();
loc.urn = loc.inner.try_strip_prefix(trail).expect("Loc must start with the given trail").len();
loc.uri = loc.inner.strip_prefix(base).expect("Loc must start with the given base").len();
loc.urn = loc.inner.strip_prefix(trail).expect("Loc must start with the given trail").len();
loc
}
@ -105,10 +106,10 @@ where
bail!("URI exceeds the entire URL");
}
if i == urn {
loc.urn = loc.try_strip_prefix(it.clone())?.len();
loc.urn = loc.strip_prefix(it.clone()).unwrap().len();
}
if i == uri {
loc.uri = loc.try_strip_prefix(it)?.len();
loc.uri = loc.strip_prefix(it).unwrap().len();
break;
}
}
@ -120,19 +121,20 @@ where
T: AsPathView<'p, P>,
{
let path = path.as_path_view();
let Some(name) = path.name() else {
let uri = path.len();
let Some(name) = path.file_name() else {
let uri = path.strip_prefix(P::empty()).unwrap().len();
return Self { inner: path, uri, urn: 0, _phantom: PhantomData };
};
let name_len = name.len();
let prefix_len =
unsafe { name.encoded_bytes().as_ptr().offset_from_unsigned(path.encoded_bytes().as_ptr()) };
let prefix_len = unsafe {
name.as_encoded_bytes().as_ptr().offset_from_unsigned(path.as_encoded_bytes().as_ptr())
};
let bytes = path.encoded_bytes();
let bytes = &path.as_encoded_bytes()[..prefix_len + name_len];
Self {
inner: unsafe { P::from_encoded_bytes(&bytes[..prefix_len + name_len]) },
uri: name_len,
inner: unsafe { P::from_encoded_bytes_unchecked(bytes) },
uri: bytes.len(),
urn: name_len,
_phantom: PhantomData,
}
@ -153,7 +155,7 @@ where
S: AsStrandView<'a, P::Strand<'a>>,
{
let mut loc = Self::bare(path);
loc.uri = loc.inner.try_strip_prefix(base).expect("Loc must start with the given base").len();
loc.uri = loc.inner.strip_prefix(base).expect("Loc must start with the given base").len();
loc
}
@ -173,29 +175,35 @@ where
pub fn as_loc(self) -> Self { self }
#[inline]
pub fn as_path(self) -> P { self.inner }
pub fn as_inner(self) -> P { self.inner }
#[inline]
pub fn is_empty(self) -> bool { self.inner.is_empty() }
pub fn is_empty(self) -> bool { self.inner.len() == 0 }
#[inline]
pub fn uri(self) -> P {
unsafe {
P::from_encoded_bytes(self.inner.encoded_bytes().get_unchecked(self.inner.len() - self.uri..))
P::from_encoded_bytes_unchecked(
self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.uri..),
)
}
}
#[inline]
pub fn urn(self) -> P {
unsafe {
P::from_encoded_bytes(self.inner.encoded_bytes().get_unchecked(self.inner.len() - self.urn..))
P::from_encoded_bytes_unchecked(
self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.urn..),
)
}
}
#[inline]
pub fn base(self) -> P {
unsafe {
P::from_encoded_bytes(self.inner.encoded_bytes().get_unchecked(..self.inner.len() - self.uri))
P::from_encoded_bytes_unchecked(
self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.uri),
)
}
}
@ -205,7 +213,9 @@ where
#[inline]
pub fn trail(self) -> P {
unsafe {
P::from_encoded_bytes(self.inner.encoded_bytes().get_unchecked(..self.inner.len() - self.urn))
P::from_encoded_bytes_unchecked(
self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.urn),
)
}
}
@ -222,9 +232,9 @@ where
unsafe {
(
P::from_encoded_bytes(self.inner.encoded_bytes().get_unchecked(base)),
P::from_encoded_bytes(self.inner.encoded_bytes().get_unchecked(rest)),
P::from_encoded_bytes(self.inner.encoded_bytes().get_unchecked(urn)),
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(base)),
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(rest)),
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(urn)),
)
}
}

View file

@ -1 +1,3 @@
yazi_macro::mod_flat!(buf loc);
#![allow(private_bounds)]
yazi_macro::mod_flat!(able buf loc);

View file

@ -1,62 +1,128 @@
use std::{fmt::Debug, hash::Hash};
use std::ffi::{OsStr, OsString};
use crate::{path::{AsPath, AsPathView, PathBufDyn, PathLike, SetNameError, StartsWithError}, strand::{AsStrandView, StrandLike}};
use hashbrown::Equivalent;
use serde::Serialize;
pub trait PathBufLike
where
Self: 'static + AsPath,
{
type Strand<'a>: StrandLike<'a>;
type Borrowed<'a>: PathLike<'a> + AsPathView<'a, Self::Borrowed<'a>> + Debug + Hash;
use crate::{FromWtf8, path::{AsPath, PathBufDynError, PathDyn, PathKind, SetNameError}, strand::{AsStrand, Strand, StrandError}};
fn borrow(&self) -> Self::Borrowed<'_>;
fn encoded_bytes(&self) -> &[u8] { self.borrow().encoded_bytes() }
fn into_dyn(self) -> PathBufDyn;
fn into_encoded_bytes(self) -> Vec<u8>;
fn is_empty(&self) -> bool { self.borrow().is_empty() }
fn len(&self) -> usize { self.borrow().len() }
fn to_str(&self) -> Result<&str, std::str::Utf8Error> { self.borrow().to_str() }
fn try_set_name<'a, T>(&mut self, name: T) -> Result<(), SetNameError>
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn try_starts_with<'a, T>(&self, base: T) -> Result<bool, StartsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn take(&mut self) -> Self;
// --- PathBufDyn
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(untagged)]
pub enum PathBufDyn {
Os(std::path::PathBuf),
}
impl PathBufLike for std::path::PathBuf {
type Borrowed<'a> = &'a std::path::Path;
type Strand<'a> = &'a std::ffi::OsStr;
fn borrow(&self) -> Self::Borrowed<'_> { self.as_path() }
fn into_dyn(self) -> PathBufDyn { PathBufDyn::Os(self) }
fn into_encoded_bytes(self) -> Vec<u8> { self.into_os_string().into_encoded_bytes() }
fn try_set_name<'a, T>(&mut self, name: T) -> Result<(), SetNameError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(self.set_file_name(name.as_strand_view()))
}
fn try_starts_with<'a, T>(&self, base: T) -> Result<bool, StartsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(self.starts_with(base.as_strand_view()))
}
fn take(&mut self) -> Self { std::mem::take(self) }
impl From<std::path::PathBuf> for PathBufDyn {
fn from(value: std::path::PathBuf) -> Self { Self::Os(value) }
}
impl From<PathDyn<'_>> for PathBufDyn {
fn from(value: PathDyn<'_>) -> Self { value.to_owned() }
}
impl TryFrom<PathBufDyn> for std::path::PathBuf {
type Error = PathBufDynError;
fn try_from(value: PathBufDyn) -> Result<Self, Self::Error> { value.into_os() }
}
impl PartialEq<PathDyn<'_>> for PathBufDyn {
fn eq(&self, other: &PathDyn<'_>) -> bool { self.as_path() == *other }
}
impl PartialEq<PathDyn<'_>> for &PathBufDyn {
fn eq(&self, other: &PathDyn<'_>) -> bool { self.as_path() == *other }
}
impl Equivalent<PathDyn<'_>> for PathBufDyn {
fn equivalent(&self, key: &PathDyn<'_>) -> bool { self.as_path() == *key }
}
impl PathBufDyn {
#[inline]
pub unsafe fn from_encoded_bytes<K>(kind: K, bytes: Vec<u8>) -> Self
where
K: Into<PathKind>,
{
match kind.into() {
PathKind::Os => Self::Os(unsafe { OsString::from_encoded_bytes_unchecked(bytes) }.into()),
}
}
pub fn into_encoded_bytes(self) -> Vec<u8> {
match self {
Self::Os(p) => p.into_os_string().into_encoded_bytes(),
}
}
#[inline]
pub fn into_os(self) -> Result<std::path::PathBuf, PathBufDynError> {
Ok(match self {
Self::Os(p) => p,
})
}
#[inline]
pub fn new(kind: PathKind) -> Self {
match kind {
PathKind::Os => Self::Os(std::path::PathBuf::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>
where
T: IntoIterator,
T::Item: AsPath,
{
for p in paths {
self.try_push(p)?;
}
Ok(())
}
pub fn try_push<T>(&mut self, path: T) -> Result<(), StrandError>
where
T: AsPath,
{
let path = path.as_path();
Ok(match self {
Self::Os(p) => p.push(path.as_os()?),
})
}
pub fn try_set_name<T>(&mut self, name: T) -> Result<(), SetNameError>
where
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::Bytes(b)) => {
p.set_file_name(OsStr::from_wtf8(b).map_err(|_| SetNameError::FromWtf8)?)
}
})
}
pub fn with<K, T>(kind: K, strand: T) -> Result<Self, StrandError>
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()?)),
})
}
pub fn with_capacity<K>(kind: K, capacity: usize) -> Self
where
K: Into<PathKind>,
{
match kind.into() {
PathKind::Os => Self::Os(std::path::PathBuf::with_capacity(capacity)),
}
}
}

View file

@ -1,96 +1,50 @@
use std::{borrow::Cow, ffi::OsStr};
use std::ffi::OsStr;
use super::{PathBufDyn, PathDyn};
use crate::path::{PathBufLike, PathCow, PathLike};
use crate::path::PathCow;
// --- AsPath
pub trait AsPath {
fn as_path(&self) -> impl PathLike<'_>;
fn as_path(&self) -> PathDyn<'_>;
}
impl AsPath for OsStr {
fn as_path(&self) -> impl PathLike<'_> { std::path::Path::new(self) }
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self.as_ref()) }
}
impl AsPath for &OsStr {
fn as_path(&self) -> impl PathLike<'_> { std::path::Path::new(self) }
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self.as_ref()) }
}
impl AsPath for std::path::Path {
fn as_path(&self) -> impl PathLike<'_> { self }
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self) }
}
impl AsPath for std::path::PathBuf {
fn as_path(&self) -> impl PathLike<'_> { self.as_path() }
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self) }
}
impl AsPath for PathDyn<'_> {
fn as_path(&self) -> impl PathLike<'_> { *self }
fn as_path(&self) -> PathDyn<'_> { *self }
}
impl AsPath for PathBufDyn {
fn as_path(&self) -> impl PathLike<'_> { self.borrow() }
fn as_path(&self) -> PathDyn<'_> {
match self {
Self::Os(p) => PathDyn::Os(p),
}
}
}
impl AsPath for &PathBufDyn {
fn as_path(&self) -> impl PathLike<'_> { self.borrow() }
fn as_path(&self) -> PathDyn<'_> { (*self).as_path() }
}
impl AsPath for PathCow<'_> {
fn as_path(&self) -> impl PathLike<'_> {
fn as_path(&self) -> PathDyn<'_> {
match self {
PathCow::Borrowed(p) => *p,
PathCow::Owned(p) => p.into(),
}
}
}
// --- AsPathDyn
pub trait AsPathDyn {
fn as_path_dyn(&self) -> PathDyn<'_>;
}
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::Path {
fn as_path_dyn(&self) -> PathDyn<'_> { 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() }
}
impl AsPathDyn for PathCow<'_> {
fn as_path_dyn(&self) -> PathDyn<'_> {
match self {
Self::Borrowed(p) => p.as_path_dyn(),
Self::Owned(p) => p.as_path_dyn(),
PathCow::Owned(p) => p.as_path(),
}
}
}
@ -101,5 +55,5 @@ pub trait AsPathRef<'a> {
}
impl<'a> AsPathRef<'a> for PathDyn<'a> {
fn as_path_ref(self) -> PathDyn<'a> { self }
fn as_path_ref(self) -> Self { self }
}

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use anyhow::Result;
use crate::{IntoOsStr, path::{AsPathDyn, PathBufDyn, PathBufLike, PathDyn, PathLike}};
use crate::{IntoOsStr, path::{AsPath, PathBufDyn, PathDyn}};
// --- PathCow
pub enum PathCow<'a> {
@ -25,8 +25,8 @@ impl From<PathCow<'_>> for PathBufDyn {
impl PartialEq<&str> for PathCow<'_> {
fn eq(&self, other: &&str) -> bool {
match self {
Self::Borrowed(s) => s.as_path_dyn() == *other,
Self::Owned(s) => s.as_path_dyn() == *other,
Self::Borrowed(s) => s.as_path() == *other,
Self::Owned(s) => s.as_path() == *other,
}
}
}
@ -41,16 +41,8 @@ impl<'a> PathCow<'a> {
pub fn into_owned(self) -> PathBufDyn {
match self {
Self::Borrowed(s) => s.to_buf_dyn(),
Self::Borrowed(s) => s.to_owned(),
Self::Owned(s) => s,
}
}
// FIXME: remove, instead implement PathLike for PathCow
pub fn encoded_bytes(&self) -> &[u8] {
match self {
Self::Borrowed(s) => s.encoded_bytes(),
Self::Owned(s) => s.encoded_bytes(),
}
}
}

View file

@ -1,411 +0,0 @@
use std::ffi::OsStr;
use anyhow::Result;
use hashbrown::Equivalent;
use serde::Serialize;
use super::{AsPathView, RsplitOnceError, StartsWithError};
use crate::{FromWtf8, Utf8BytePredictor, path::{AsPathDyn, EndsWithError, JoinError, PathBufDynError, PathBufLike, PathBufUnsafeExt, PathDynError, PathKind, PathLike, SetNameError, StripPrefixError}, scheme::SchemeKind, strand::{AsStrandDyn, AsStrandView, Strand, StrandError}};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum PathDyn<'p> {
Os(&'p std::path::Path),
}
impl<'a> AsPathView<'a, PathDyn<'a>> for PathDyn<'a> {
fn as_path_view(self) -> PathDyn<'a> { self }
}
impl<'a> AsPathView<'a, PathDyn<'a>> for std::path::Components<'a> {
fn as_path_view(self) -> PathDyn<'a> { PathDyn::Os(self.as_path()) }
}
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 PathBufDyn> for PathDyn<'a> {
fn from(value: &'a PathBufDyn) -> Self { value.borrow() }
}
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 PartialEq<&std::path::Path> for PathDyn<'_> {
fn eq(&self, other: &&std::path::Path) -> bool { matches!(*self, PathDyn::Os(p) if p == *other) }
}
impl PartialEq<&str> for PathDyn<'_> {
fn eq(&self, other: &&str) -> bool {
match self {
PathDyn::Os(p) => p == other,
}
}
}
impl Equivalent<PathBufDyn> for PathDyn<'_> {
fn equivalent(&self, key: &PathBufDyn) -> bool { *self == key.borrow() }
}
impl<'p> PathLike<'p> for PathDyn<'p> {
type Components<'a> = std::path::Components<'a>;
type Display<'a> = std::path::Display<'a>;
type Owned = PathBufDyn;
type Strand<'a> = Strand<'a>;
type View<'a> = PathDyn<'a>;
fn as_dyn(self) -> PathDyn<'p> { self }
fn components(self) -> Self::Components<'p> {
match self {
Self::Os(p) => p.components(),
}
}
// 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(),
}
}
fn ext(self) -> Option<Self::Strand<'p>> {
Some(match self {
Self::Os(p) => p.extension()?.into(),
})
}
fn has_root(self) -> bool {
match self {
Self::Os(p) => p.has_root(),
}
}
fn is_absolute(self) -> bool {
match self {
Self::Os(p) => p.is_absolute(),
}
}
fn kind(self) -> PathKind {
match self {
Self::Os(_) => PathKind::Os,
}
}
fn name(self) -> Option<Self::Strand<'p>> {
Some(match self {
Self::Os(p) => p.file_name()?.into(),
})
}
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()?),
})
}
fn stem(self) -> Option<Self::Strand<'p>> {
Some(match self {
Self::Os(p) => p.file_stem()?.into(),
})
}
fn try_ends_with<'a, T>(self, child: T) -> Result<bool, EndsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(match (self, child.as_strand_view()) {
(Self::Os(p), Strand::Os(q)) => p.ends_with(q),
(Self::Os(p), Strand::Utf8(q)) => p.ends_with(q),
(Self::Os(p), Strand::Bytes(b)) => {
p.ends_with(OsStr::from_wtf8(b).map_err(|_| EndsWithError)?)
}
})
}
fn try_join<'a, T>(self, path: T) -> Result<Self::Owned, JoinError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(match (self, path.as_strand_view()) {
(Self::Os(p), Strand::Os(q)) => Self::Owned::Os(p.join(q)),
(Self::Os(p), Strand::Utf8(q)) => Self::Owned::Os(p.join(q)),
(Self::Os(p), Strand::Bytes(b)) => {
Self::Owned::Os(p.join(OsStr::from_wtf8(b).map_err(|_| JoinError::FromWtf8)?))
}
})
}
fn rsplit_pred<'a, T>(self, pred: T) -> Option<(Self, Self)>
where
T: Utf8BytePredictor,
{
match self {
PathDyn::Os(p) => p.rsplit_pred(pred).map(|(l, r)| (Self::Os(l), Self::Os(r))),
}
}
fn try_rsplit_seq<'a, T>(self, pat: T) -> Result<(Self, Self), RsplitOnceError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
let pat = pat.as_strand_view();
match self {
PathDyn::Os(p) => {
let (l, r) = p.try_rsplit_seq(pat.as_os().map_err(|_| RsplitOnceError::AsOs)?)?;
Ok((Self::Os(l), Self::Os(r)))
}
}
}
fn try_starts_with<'a, T>(self, base: T) -> Result<bool, StartsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(match (self, base.as_strand_view()) {
(Self::Os(p), Strand::Os(q)) => p.starts_with(q),
(Self::Os(p), Strand::Utf8(q)) => p.starts_with(q),
(Self::Os(p), Strand::Bytes(b)) => {
p.starts_with(OsStr::from_wtf8(b).map_err(|_| StartsWithError)?)
}
})
}
fn try_strip_prefix<'a, T>(self, base: T) -> Result<Self, StripPrefixError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(match (self, base.as_strand_view()) {
(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::Bytes(b)) => {
Self::Os(p.strip_prefix(OsStr::from_wtf8(b).map_err(|_| StripPrefixError::WrongEncoding)?)?)
}
})
}
}
impl<'a> PathDyn<'a> {
#[inline]
pub fn os<T>(path: &'a T) -> Self
where
T: ?Sized + AsRef<std::path::Path>,
{
Self::Os(path.as_ref())
}
#[inline]
pub fn as_os(self) -> Result<&'a std::path::Path, PathDynError> {
match self {
Self::Os(p) => Ok(p),
}
}
#[inline]
pub fn to_os_owned(self) -> Result<std::path::PathBuf, PathDynError> {
match self {
Self::Os(p) => Ok(p.to_owned()),
}
}
pub fn with<T>(kind: SchemeKind, strand: &'a T) -> Result<Self, StrandError>
where
T: ?Sized + AsStrandDyn,
{
use SchemeKind as K;
let s = strand.as_strand_dyn();
Ok(match kind {
K::Regular | K::Search | K::Archive => Self::Os(std::path::Path::new(s.as_os()?)),
K::Sftp => Self::Os(std::path::Path::new(s.as_os()?)), // FIXME
})
}
#[inline]
pub unsafe fn from_encoded_bytes(kind: impl Into<PathKind>, bytes: &'a [u8]) -> Self {
match kind.into() {
PathKind::Os => Self::Os(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }.as_ref()),
}
}
}
// --- PathBufDyn
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(untagged)]
pub enum PathBufDyn {
Os(std::path::PathBuf),
}
impl From<std::path::PathBuf> for PathBufDyn {
fn from(value: std::path::PathBuf) -> Self { Self::Os(value) }
}
impl From<&PathBufDyn> for PathBufDyn {
fn from(value: &PathBufDyn) -> Self { value.clone() }
}
impl TryFrom<PathBufDyn> for std::path::PathBuf {
type Error = PathBufDynError;
fn try_from(value: PathBufDyn) -> Result<Self, Self::Error> { value.into_os() }
}
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 }
}
impl PathBufLike for PathBufDyn {
type Borrowed<'a> = PathDyn<'a>;
type Strand<'a> = Strand<'a>;
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(),
}
}
fn into_dyn(self) -> PathBufDyn { self }
fn into_encoded_bytes(self) -> Vec<u8> {
match self {
Self::Os(p) => p.into_os_string().into_encoded_bytes(),
}
}
fn try_set_name<'a, T>(&mut self, name: T) -> Result<(), SetNameError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(match (self, name.as_strand_view()) {
(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::Bytes(b)) => {
p.set_file_name(OsStr::from_wtf8(b).map_err(|_| SetNameError::FromWtf8)?)
}
})
}
fn try_starts_with<'a, T>(&self, base: T) -> Result<bool, StartsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(match (self, base.as_strand_view()) {
(Self::Os(p), Strand::Os(q)) => p.starts_with(q),
(Self::Os(p), Strand::Utf8(q)) => p.starts_with(q),
(Self::Os(p), Strand::Bytes(b)) => {
p.starts_with(OsStr::from_wtf8(b).map_err(|_| StartsWithError)?)
}
})
}
fn take(&mut self) -> Self {
match self {
Self::Os(p) => Self::Os(std::mem::take(p)),
}
}
}
impl PathBufDyn {
#[inline]
pub fn os(path: impl Into<std::path::PathBuf>) -> Self { Self::Os(path.into()) }
#[inline]
pub fn into_os(self) -> Result<std::path::PathBuf, PathBufDynError> {
Ok(match self {
PathBufDyn::Os(p) => p,
})
}
#[inline]
pub fn new(kind: SchemeKind) -> Self {
use SchemeKind as K;
match kind {
K::Regular | K::Search | K::Archive => Self::Os(std::path::PathBuf::new()),
K::Sftp => Self::Os(std::path::PathBuf::new()), // FIXME
}
}
pub fn with<T>(kind: SchemeKind, strand: T) -> Result<Self, StrandError>
where
T: AsStrandDyn,
{
use SchemeKind as K;
let s = strand.as_strand_dyn();
Ok(match kind {
K::Regular | K::Search | K::Archive => Self::Os(std::path::PathBuf::from(s.as_os()?)),
K::Sftp => Self::Os(std::path::PathBuf::from(s.as_os()?)), // FIXME
})
}
pub fn with_capacity(kind: SchemeKind, capacity: usize) -> Self {
use SchemeKind as K;
match kind {
K::Regular | K::Search | K::Archive => Self::Os(std::path::PathBuf::with_capacity(capacity)),
K::Sftp => Self::Os(std::path::PathBuf::with_capacity(capacity)), // FIXME
}
}
pub fn try_push<T>(&mut self, path: T) -> Result<(), StrandError>
where
T: AsPathDyn,
{
let path = path.as_path_dyn();
Ok(match self {
PathBufDyn::Os(p) => p.push(path.as_os()?),
})
}
pub fn try_extend<T>(&mut self, paths: T) -> Result<(), StrandError>
where
T: IntoIterator,
T::Item: AsPathDyn,
{
for p in paths {
self.try_push(p)?;
}
Ok(())
}
#[inline]
pub unsafe fn from_encoded_bytes(kind: impl Into<PathKind>, bytes: Vec<u8>) -> Self {
match kind.into() {
PathKind::Os => Self::Os(unsafe { std::path::PathBuf::from_encoded_bytes(bytes) }),
}
}
}

View file

@ -16,7 +16,7 @@ pub enum JoinError {
}
impl From<JoinError> for std::io::Error {
fn from(err: JoinError) -> Self { std::io::Error::other(err) }
fn from(err: JoinError) -> Self { Self::other(err) }
}
// --- PathDynError
@ -27,7 +27,7 @@ pub enum PathDynError {
}
impl From<PathDynError> for std::io::Error {
fn from(err: PathDynError) -> Self { std::io::Error::other(err) }
fn from(err: PathDynError) -> Self { Self::other(err) }
}
// --- PathBufDynError
@ -46,7 +46,7 @@ pub enum SetNameError {
}
impl From<SetNameError> for std::io::Error {
fn from(err: SetNameError) -> Self { std::io::Error::other(err) }
fn from(err: SetNameError) -> Self { Self::other(err) }
}
// --- RsplitOnce
@ -61,6 +61,15 @@ pub enum RsplitOnceError {
NotFound,
}
impl From<StrandError> for RsplitOnceError {
fn from(err: StrandError) -> Self {
match err {
StrandError::AsOs => Self::AsOs,
StrandError::AsUtf8 => Self::AsUtf8,
}
}
}
// --- StartsWithError
#[derive(Error, Debug)]
#[error("calling starts_with on paths with different encodings")]

View file

@ -0,0 +1,96 @@
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}};
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 display(&self) -> std::path::Display<'_> { self.as_path().display() }
fn encoded_bytes(&self) -> &[u8] { self.as_path().encoded_bytes() }
fn ext(&self) -> Option<Strand<'_>> { self.as_path().ext() }
fn has_root(&self) -> bool { self.as_path().has_root() }
fn is_absolute(&self) -> bool { self.as_path().is_absolute() }
fn is_empty(&self) -> bool { self.as_path().is_empty() }
#[cfg(unix)]
fn is_hidden(&self) -> bool { self.as_path().is_hidden() }
fn kind(&self) -> PathKind { self.as_path().kind() }
fn len(&self) -> usize { self.as_path().len() }
fn name(&self) -> Option<Strand<'_>> { self.as_path().name() }
fn parent(&self) -> Option<PathDyn<'_>> { self.as_path().parent() }
fn rsplit_pred<T>(&self, pred: T) -> Option<(PathDyn<'_>, PathDyn<'_>)>
where
T: Utf8BytePredictor,
{
self.as_path().rsplit_pred(pred)
}
fn stem(&self) -> Option<Strand<'_>> { self.as_path().stem() }
fn to_os_owned(&self) -> Result<std::path::PathBuf, PathDynError> { self.as_path().to_os_owned() }
fn to_owned(&self) -> PathBufDyn { self.as_path().to_owned() }
fn to_str(&self) -> Result<&str, std::str::Utf8Error> { self.as_path().to_str() }
fn to_string_lossy(&self) -> Cow<'_, str> { self.as_path().to_string_lossy() }
fn try_ends_with<T>(&self, child: T) -> Result<bool, EndsWithError>
where
T: AsStrand,
{
self.as_path().try_ends_with(child)
}
fn try_join<T>(&self, path: T) -> Result<PathBufDyn, JoinError>
where
T: AsStrand,
{
self.as_path().try_join(path)
}
fn try_rsplit_seq<T>(&self, pat: T) -> Result<(PathDyn<'_>, PathDyn<'_>), RsplitOnceError>
where
T: AsStrand,
{
self.as_path().try_rsplit_seq(pat)
}
fn try_starts_with<T>(&self, base: T) -> Result<bool, StartsWithError>
where
T: AsStrand,
{
self.as_path().try_starts_with(base)
}
fn try_strip_prefix<T>(&self, base: T) -> Result<PathDyn<'_>, StripPrefixError>
where
T: AsStrand,
{
self.as_path().try_strip_prefix(base)
}
}
impl<P> From<&P> for PathBufDyn
where
P: PathLike,
{
fn from(value: &P) -> Self { value.to_owned() }
}
impl PathLike for PathBufDyn {}
impl PathLike for PathCow<'_> {}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(buf component components conversion cow error kind path r#dyn r#unsafe view);
yazi_macro::mod_flat!(buf component components conversion cow error kind like path view);

View file

@ -1,184 +1,250 @@
use std::{borrow::Cow, ffi::OsStr};
use anyhow::Result;
use hashbrown::Equivalent;
use crate::{BytesExt, Utf8BytePredictor, path::{AsPathView, EndsWithError, JoinError, PathBufDyn, PathBufLike, PathDyn, PathKind, RsplitOnceError, StartsWithError, StripPrefixError}, strand::{AsStrandView, StrandLike}};
use super::{RsplitOnceError, StartsWithError};
use crate::{BytesExt, FromWtf8, Utf8BytePredictor, path::{AsPath, EndsWithError, JoinError, PathBufDyn, PathDynError, PathKind, StripPrefixError}, strand::{AsStrand, Strand, StrandError}};
pub trait PathLike<'p>
where
Self: Copy + AsPathView<'p, Self::View<'p>> + AsStrandView<'p, Self::Strand<'p>>,
{
type Strand<'a>: StrandLike<'a>;
type Owned: PathBufLike + Into<Self::Owned>;
type View<'a>;
type Components<'a>: Clone
+ DoubleEndedIterator
+ AsPathView<'a, Self::View<'a>>
+ AsStrandView<'a, Self::Strand<'a>>;
type Display<'a>: std::fmt::Display;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum PathDyn<'p> {
Os(&'p std::path::Path),
}
fn as_dyn(self) -> PathDyn<'p>;
impl<'a> From<&'a std::path::Path> for PathDyn<'a> {
fn from(value: &'a std::path::Path) -> Self { Self::Os(value) }
}
fn components(self) -> Self::Components<'p>;
impl<'a> From<&'a PathBufDyn> for PathDyn<'a> {
fn from(value: &'a PathBufDyn) -> Self { value.as_path() }
}
fn default() -> Self;
impl PartialEq<PathBufDyn> for PathDyn<'_> {
fn eq(&self, other: &PathBufDyn) -> bool { *self == other.as_path() }
}
fn display(self) -> Self::Display<'p>;
impl PartialEq<PathDyn<'_>> for &std::path::Path {
fn eq(&self, other: &PathDyn<'_>) -> bool { matches!(*other, PathDyn::Os(p) if p == *self) }
}
fn encoded_bytes(self) -> &'p [u8];
impl PartialEq<&std::path::Path> for PathDyn<'_> {
fn eq(&self, other: &&std::path::Path) -> bool { matches!(*self, PathDyn::Os(p) if p == *other) }
}
fn ext(self) -> Option<Self::Strand<'p>>;
impl PartialEq<&str> for PathDyn<'_> {
fn eq(&self, other: &&str) -> bool {
match self {
PathDyn::Os(p) => p == other,
}
}
}
fn has_root(self) -> bool;
impl Equivalent<PathBufDyn> for PathDyn<'_> {
fn equivalent(&self, key: &PathBufDyn) -> bool { *self == key.as_path() }
}
fn is_absolute(self) -> bool;
impl<'p> PathDyn<'p> {
#[inline]
pub fn as_os(self) -> Result<&'p std::path::Path, PathDynError> {
match self {
Self::Os(p) => Ok(p),
}
}
fn is_empty(self) -> bool { self.encoded_bytes().is_empty() }
pub fn components(self) -> std::path::Components<'p> {
match self {
Self::Os(p) => p.components(),
}
}
pub fn display(self) -> std::path::Display<'p> {
match self {
Self::Os(p) => p.display(),
}
}
pub fn encoded_bytes(self) -> &'p [u8] {
match self {
Self::Os(p) => p.as_os_str().as_encoded_bytes(),
}
}
pub fn ext(self) -> Option<Strand<'p>> {
Some(match self {
Self::Os(p) => p.extension()?.into(),
})
}
#[inline]
pub unsafe fn from_encoded_bytes<K>(kind: K, bytes: &'p [u8]) -> Self
where
K: Into<PathKind>,
{
match kind.into() {
PathKind::Os => Self::Os(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }.as_ref()),
}
}
pub fn has_root(self) -> bool {
match self {
Self::Os(p) => p.has_root(),
}
}
pub fn is_absolute(self) -> bool {
match self {
Self::Os(p) => p.is_absolute(),
}
}
pub fn is_empty(self) -> bool { self.encoded_bytes().is_empty() }
#[cfg(unix)]
fn is_hidden(self) -> bool {
pub fn is_hidden(self) -> bool {
self.name().is_some_and(|n| n.encoded_bytes().first() == Some(&b'.'))
}
fn kind(self) -> PathKind;
fn len(self) -> usize { self.encoded_bytes().len() }
fn name(self) -> Option<Self::Strand<'p>>;
fn owned(self) -> Self::Owned;
fn parent(self) -> Option<Self>;
fn rsplit_pred<T>(self, pred: T) -> Option<(Self, Self)>
where
T: Utf8BytePredictor;
fn stem(self) -> Option<Self::Strand<'p>>;
fn to_str(self) -> Result<&'p str, std::str::Utf8Error> { str::from_utf8(self.encoded_bytes()) }
fn to_string_lossy(self) -> Cow<'p, str> { String::from_utf8_lossy(self.encoded_bytes()) }
fn to_buf_dyn(self) -> PathBufDyn { self.as_dyn().owned() }
fn try_ends_with<'a, T>(self, child: T) -> Result<bool, EndsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn try_join<'a, T>(self, path: T) -> Result<Self::Owned, JoinError>
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn try_rsplit_seq<'a, T>(self, pat: T) -> Result<(Self, Self), RsplitOnceError>
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn try_starts_with<'a, T>(self, base: T) -> Result<bool, StartsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>;
fn try_strip_prefix<'a, T>(self, base: T) -> Result<Self, StripPrefixError>
where
T: AsStrandView<'a, Self::Strand<'a>>;
}
impl<'p> PathLike<'p> for &'p std::path::Path {
type Components<'a> = std::path::Components<'a>;
type Display<'a> = std::path::Display<'a>;
type Owned = std::path::PathBuf;
type Strand<'a> = &'a std::ffi::OsStr;
type View<'a> = &'a std::path::Path;
fn as_dyn(self) -> PathDyn<'p> { PathDyn::Os(self) }
fn components(self) -> Self::Components<'p> { self.components() }
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 ext(self) -> Option<Self::Strand<'p>> { self.extension() }
fn has_root(self) -> bool { self.has_root() }
fn is_absolute(self) -> bool { self.is_absolute() }
fn kind(self) -> PathKind { PathKind::Os }
fn name(self) -> Option<Self::Strand<'p>> { self.file_name() }
fn owned(self) -> Self::Owned { self.to_path_buf() }
fn parent(self) -> Option<Self> { self.parent() }
fn stem(self) -> Option<Self::Strand<'p>> { self.file_stem() }
fn try_ends_with<'a, T>(self, child: T) -> Result<bool, EndsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(self.ends_with(child.as_strand_view()))
pub fn kind(self) -> PathKind {
match self {
Self::Os(_) => PathKind::Os,
}
}
fn try_join<'a, T>(self, path: T) -> Result<Self::Owned, JoinError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
Ok(self.join(path.as_strand_view()))
pub fn len(self) -> usize { self.encoded_bytes().len() }
pub fn name(self) -> Option<Strand<'p>> {
Some(match self {
Self::Os(p) => p.file_name()?.into(),
})
}
fn rsplit_pred<'a, T>(self, pred: T) -> Option<(Self, Self)>
#[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()?),
})
}
pub fn rsplit_pred<T>(self, pred: T) -> Option<(Self, Self)>
where
T: Utf8BytePredictor,
{
let b = self.encoded_bytes();
let (left, right) = b.rsplit_pred_once(pred)?;
let (a, b) = self.encoded_bytes().rsplit_pred_once(pred)?;
Some(unsafe {
(
OsStr::from_encoded_bytes_unchecked(left).as_ref(),
OsStr::from_encoded_bytes_unchecked(right).as_ref(),
)
(Self::from_encoded_bytes(self.kind(), a), Self::from_encoded_bytes(self.kind(), b))
})
}
fn try_rsplit_seq<'a, T>(self, pat: T) -> Result<(Self, Self), RsplitOnceError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
{
let b = self.encoded_bytes();
let p = pat.as_strand_view().encoded_bytes();
pub fn stem(self) -> Option<Strand<'p>> {
Some(match self {
Self::Os(p) => p.file_stem()?.into(),
})
}
#[inline]
pub fn to_os_owned(self) -> Result<std::path::PathBuf, PathDynError> {
match self {
Self::Os(p) => Ok(p.to_owned()),
}
}
pub fn to_owned(self) -> PathBufDyn {
match self {
Self::Os(p) => PathBufDyn::Os(p.to_path_buf()),
}
}
pub fn to_str(self) -> Result<&'p str, std::str::Utf8Error> {
str::from_utf8(self.encoded_bytes())
}
pub fn to_string_lossy(self) -> Cow<'p, str> { String::from_utf8_lossy(self.encoded_bytes()) }
pub fn try_ends_with<T>(self, child: T) -> Result<bool, EndsWithError>
where
T: AsStrand,
{
Ok(match (self, child.as_strand()) {
(Self::Os(p), Strand::Os(q)) => p.ends_with(q),
(Self::Os(p), Strand::Utf8(q)) => p.ends_with(q),
(Self::Os(p), Strand::Bytes(b)) => {
p.ends_with(OsStr::from_wtf8(b).map_err(|_| EndsWithError)?)
}
})
}
pub fn try_join<T>(self, path: T) -> Result<PathBufDyn, JoinError>
where
T: AsStrand,
{
Ok(match (self, path.as_strand()) {
(Self::Os(p), Strand::Os(q)) => PathBufDyn::Os(p.join(q)),
(Self::Os(p), Strand::Utf8(q)) => PathBufDyn::Os(p.join(q)),
(Self::Os(p), Strand::Bytes(b)) => {
PathBufDyn::Os(p.join(OsStr::from_wtf8(b).map_err(|_| JoinError::FromWtf8)?))
}
})
}
pub fn try_rsplit_seq<T>(self, pat: T) -> Result<(Self, Self), RsplitOnceError>
where
T: AsStrand,
{
let pat = pat.as_strand();
let (a, b) = match self {
PathDyn::Os(p) => {
p.as_os_str().as_encoded_bytes().rsplit_seq_once(pat.as_os()?.as_encoded_bytes())
}
}
.ok_or(RsplitOnceError::NotFound)?;
let (left, right) = b.rsplit_seq_once(p).ok_or(RsplitOnceError::NotFound)?;
Ok(unsafe {
(
OsStr::from_encoded_bytes_unchecked(left).as_ref(),
OsStr::from_encoded_bytes_unchecked(right).as_ref(),
)
(Self::from_encoded_bytes(self.kind(), a), Self::from_encoded_bytes(self.kind(), b))
})
}
fn try_starts_with<'a, T>(self, base: T) -> Result<bool, StartsWithError>
pub fn try_starts_with<T>(self, base: T) -> Result<bool, StartsWithError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
T: AsStrand,
{
Ok(self.starts_with(base.as_strand_view()))
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::Bytes(b)) => {
p.starts_with(OsStr::from_wtf8(b).map_err(|_| StartsWithError)?)
}
})
}
fn try_strip_prefix<'a, T>(self, base: T) -> Result<Self, StripPrefixError>
pub fn try_strip_prefix<T>(self, base: T) -> Result<Self, StripPrefixError>
where
T: AsStrandView<'a, Self::Strand<'a>>,
T: AsStrand,
{
Ok(self.strip_prefix(base.as_strand_view())?)
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::Bytes(b)) => {
Self::Os(p.strip_prefix(OsStr::from_wtf8(b).map_err(|_| StripPrefixError::WrongEncoding)?)?)
}
})
}
pub fn with<K, T>(kind: K, strand: &'p T) -> Result<Self, StrandError>
where
K: Into<PathKind>,
T: ?Sized + AsStrand,
{
let s = strand.as_strand();
Ok(match kind.into() {
PathKind::Os => Self::Os(std::path::Path::new(s.as_os()?)),
})
}
}
impl<'a, P> From<P> for PathBufDyn
where
P: PathLike<'a>,
{
fn from(value: P) -> Self { value.to_buf_dyn() }
}

View file

@ -1,23 +0,0 @@
use std::ffi::OsString;
// --- PathUnsafeExt
pub trait PathUnsafeExt<'p> {
unsafe fn from_encoded_bytes(bytes: &'p [u8]) -> Self;
}
impl<'p> PathUnsafeExt<'p> for &'p std::path::Path {
unsafe fn from_encoded_bytes(bytes: &'p [u8]) -> Self {
std::path::Path::new(unsafe { std::ffi::OsStr::from_encoded_bytes_unchecked(bytes) })
}
}
// --- PathBufUnsafeExt
pub trait PathBufUnsafeExt {
unsafe fn from_encoded_bytes(bytes: Vec<u8>) -> Self;
}
impl PathBufUnsafeExt for std::path::PathBuf {
unsafe fn from_encoded_bytes(bytes: Vec<u8>) -> Self {
Self::from(unsafe { OsString::from_encoded_bytes_unchecked(bytes) })
}
}

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, ffi::OsStr};
use crate::path::{AsPathDyn, PathDyn};
use crate::path::{AsPath, PathDyn};
// --- AsPathView
pub trait AsPathView<'a, T> {
@ -25,9 +25,9 @@ impl<'a> AsPathView<'a, &'a std::path::Path> for std::path::Components<'a> {
impl<'a, T> AsPathView<'a, PathDyn<'a>> for &'a T
where
T: AsPathDyn,
T: AsPath,
{
fn as_path_view(self) -> PathDyn<'a> { self.as_path_dyn() }
fn as_path_view(self) -> PathDyn<'a> { self.as_path() }
}
impl<'a> AsPathView<'a, &'a std::path::Path> for &'a Cow<'_, OsStr> {

View file

@ -1,9 +1,9 @@
use std::{borrow::Cow, ops::Not};
use std::borrow::Cow;
use anyhow::{Result, ensure};
use percent_encoding::percent_decode;
use crate::{path::{AsPath, PathCow, PathLike}, pool::{InternStr, SymbolCow}, scheme::{AsScheme, Scheme, SchemeKind, SchemeRef}, url::Url};
use crate::{path::{PathCow, PathLike}, pool::{InternStr, SymbolCow}, scheme::{AsScheme, Scheme, SchemeKind, SchemeRef}, url::Url};
#[derive(Clone, Debug)]
pub enum SchemeCow<'a> {
@ -144,11 +144,10 @@ impl<'a> SchemeCow<'a> {
urn: Option<usize>,
path: &PathCow,
) -> Result<(usize, usize)> {
let path = path.as_path();
Ok(match kind {
SchemeKind::Regular => {
ensure!(uri.is_none() && urn.is_none(), "Regular scheme cannot have ports");
(path.is_empty().not() as usize, path.name().is_some() as usize)
(path.components().count(), path.name().is_some() as usize)
}
SchemeKind::Search => {
let (uri, urn) = (uri.unwrap_or(0), urn.unwrap_or(0));
@ -157,7 +156,7 @@ impl<'a> SchemeCow<'a> {
}
SchemeKind::Archive => (uri.unwrap_or(0), urn.unwrap_or(0)),
SchemeKind::Sftp => {
let uri = uri.unwrap_or(path.is_empty().not() as usize);
let uri = uri.unwrap_or_else(|| path.components().count());
let urn = urn.unwrap_or(path.name().is_some() as usize);
(uri, urn)
}
@ -165,12 +164,7 @@ impl<'a> SchemeCow<'a> {
}
pub fn retrieve_ports(url: Url) -> (usize, usize) {
match url {
Url::Regular(loc) => (loc.is_empty().not() as usize, loc.file_name().is_some() as usize),
Url::Search { loc, .. } => (loc.uri().components().count(), loc.urn().components().count()),
Url::Archive { loc, .. } => (loc.uri().components().count(), loc.urn().components().count()),
Url::Sftp { loc, .. } => (loc.uri().components().count(), loc.urn().components().count()),
}
(url.uri().components().count(), url.urn().components().count())
}
}

View file

@ -1,8 +1,8 @@
use std::{fmt::{self, Display}, ops::Not};
use std::fmt::{self, Display};
use percent_encoding::{AsciiSet, CONTROLS, PercentEncode, percent_encode};
use crate::{path::PathLike, scheme::SchemeKind, url::Url};
use crate::{scheme::SchemeKind, url::Url};
#[derive(Clone, Copy)]
pub struct Encode<'a>(pub Url<'a>);
@ -39,7 +39,7 @@ impl<'a> Encode<'a> {
SchemeKind::Regular => Ok(()),
SchemeKind::Search | SchemeKind::Archive => w!(0, 0),
SchemeKind::Sftp => {
w!(self.0.0.loc().is_empty().not() as usize, self.0.0.loc().name().is_some() as usize)
w!(self.0.0.loc().components().count(), self.0.0.loc().name().is_some() as usize)
}
}
}

View file

@ -13,10 +13,10 @@ impl AsScheme for Scheme {
#[inline]
fn as_scheme(&self) -> SchemeRef<'_> {
match *self {
Scheme::Regular { uri, urn } => SchemeRef::Regular { uri, urn },
Scheme::Search { ref domain, uri, urn } => SchemeRef::Search { domain, uri, urn },
Scheme::Archive { ref domain, uri, urn } => SchemeRef::Archive { domain, uri, urn },
Scheme::Sftp { ref domain, uri, urn } => SchemeRef::Sftp { domain, uri, urn },
Self::Regular { uri, urn } => SchemeRef::Regular { uri, urn },
Self::Search { ref domain, uri, urn } => SchemeRef::Search { domain, uri, urn },
Self::Archive { ref domain, uri, urn } => SchemeRef::Archive { domain, uri, urn },
Self::Sftp { ref domain, uri, urn } => SchemeRef::Sftp { domain, uri, urn },
}
}
}

View file

@ -0,0 +1,85 @@
use std::ffi::OsString;
use serde::Serialize;
use crate::{path::PathDyn, scheme::SchemeKind, strand::{AsStrand, Strand, StrandError, StrandKind}};
// --- StrandBuf
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(untagged)]
pub enum StrandBuf {
Os(OsString),
Utf8(String),
Bytes(Vec<u8>),
}
impl Default for StrandBuf {
fn default() -> Self { Self::Utf8(String::new()) }
}
impl From<OsString> for StrandBuf {
fn from(value: OsString) -> Self { Self::Os(value) }
}
impl From<String> for StrandBuf {
fn from(value: String) -> Self { Self::Utf8(value) }
}
impl From<PathDyn<'_>> for StrandBuf {
fn from(value: PathDyn) -> Self {
match value {
PathDyn::Os(p) => Self::Os(p.as_os_str().to_owned()),
}
}
}
impl PartialEq<Strand<'_>> for StrandBuf {
fn eq(&self, other: &Strand<'_>) -> bool { self.as_strand() == *other }
}
impl StrandBuf {
pub fn clear(&mut self) {
match self {
Self::Os(buf) => buf.clear(),
Self::Utf8(buf) => buf.clear(),
Self::Bytes(buf) => buf.clear(),
}
}
#[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::Bytes => Self::Bytes(bytes),
}
}
pub fn into_encoded_bytes(self) -> Vec<u8> {
match self {
Self::Os(s) => s.into_encoded_bytes(),
Self::Utf8(s) => s.into_bytes(),
Self::Bytes(b) => b,
}
}
pub fn try_push<T>(&mut self, s: T) -> Result<(), StrandError>
where
T: AsStrand,
{
let s = s.as_strand();
Ok(match self {
Self::Os(buf) => buf.push(s.as_os()?),
Self::Utf8(buf) => buf.push_str(s.as_utf8()?),
Self::Bytes(buf) => buf.extend(s.encoded_bytes()),
})
}
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
}
}
}

View file

@ -1,159 +1,103 @@
use std::{borrow::Cow, ffi::{OsStr, OsString}};
use crate::{path::{PathBufDyn, PathDyn}, strand::{Strand, StrandBuf, StrandBufLike, StrandCow, StrandLike}};
use crate::{path::{PathBufDyn, PathDyn}, strand::{Strand, StrandBuf, StrandCow}};
// --- AsStrand
pub trait AsStrand {
fn as_strand(&self) -> impl StrandLike<'_>;
fn as_strand(&self) -> Strand<'_>;
}
impl AsStrand for [u8] {
fn as_strand(&self) -> impl StrandLike<'_> { self }
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
}
impl AsStrand for &[u8] {
fn as_strand(&self) -> impl StrandLike<'_> { *self }
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
}
impl AsStrand for Vec<u8> {
fn as_strand(&self) -> impl StrandLike<'_> { self.as_slice() }
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
}
impl AsStrand for str {
fn as_strand(&self) -> impl StrandLike<'_> { self }
fn as_strand(&self) -> Strand<'_> { Strand::Utf8(self) }
}
impl AsStrand for &str {
fn as_strand(&self) -> impl StrandLike<'_> { *self }
fn as_strand(&self) -> Strand<'_> { Strand::Utf8(self) }
}
impl AsStrand for String {
fn as_strand(&self) -> impl StrandLike<'_> { self.as_str() }
fn as_strand(&self) -> Strand<'_> { Strand::Utf8(self) }
}
impl AsStrand for &String {
fn as_strand(&self) -> Strand<'_> { Strand::Utf8(self) }
}
impl AsStrand for OsStr {
fn as_strand(&self) -> impl StrandLike<'_> { self }
fn as_strand(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrand for &OsStr {
fn as_strand(&self) -> impl StrandLike<'_> { *self }
fn as_strand(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrand for OsString {
fn as_strand(&self) -> impl StrandLike<'_> { self.as_os_str() }
fn as_strand(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrand for &std::path::Path {
fn as_strand(&self) -> Strand<'_> { Strand::Os(self.as_os_str()) }
}
impl AsStrand for Cow<'_, OsStr> {
fn as_strand(&self) -> impl StrandLike<'_> { AsRef::<OsStr>::as_ref(self) }
fn as_strand(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrand for PathDyn<'_> {
fn as_strand(&self) -> impl StrandLike<'_> {
match self {
Self::Os(p) => p.as_os_str(),
}
}
}
impl AsStrand for &PathBufDyn {
fn as_strand(&self) -> impl StrandLike<'_> {
match self {
PathBufDyn::Os(p) => p.as_os_str(),
}
}
}
impl AsStrand for Strand<'_> {
fn as_strand(&self) -> impl StrandLike<'_> { *self }
}
impl AsStrand for StrandBuf {
fn as_strand(&self) -> impl StrandLike<'_> { self.borrow() }
}
impl AsStrand for StrandCow<'_> {
fn as_strand(&self) -> impl StrandLike<'_> {
match self {
StrandCow::Borrowed(s) => *s,
StrandCow::Owned(s) => s.into(),
}
}
}
impl AsStrand for &StrandCow<'_> {
fn as_strand(&self) -> impl StrandLike<'_> { (**self).as_strand() }
}
// --- AsStrandDyn
pub trait AsStrandDyn {
fn as_strand_dyn(&self) -> Strand<'_>;
}
impl AsStrandDyn for OsStr {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrandDyn for &OsStr {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrandDyn for OsString {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrandDyn for &std::path::PathBuf {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Os(self.as_os_str()) }
}
impl AsStrandDyn for &str {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Utf8(self) }
}
impl AsStrandDyn for String {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Utf8(self) }
}
impl AsStrandDyn for &String {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Utf8(self) }
}
impl AsStrandDyn for Cow<'_, OsStr> {
fn as_strand_dyn(&self) -> Strand<'_> { Strand::Os(self) }
}
impl AsStrandDyn for PathDyn<'_> {
fn as_strand_dyn(&self) -> Strand<'_> {
fn as_strand(&self) -> Strand<'_> {
match self {
Self::Os(p) => Strand::Os(p.as_os_str()),
}
}
}
impl AsStrandDyn for &PathBufDyn {
fn as_strand_dyn(&self) -> Strand<'_> {
impl AsStrand for &PathBufDyn {
fn as_strand(&self) -> Strand<'_> {
match self {
PathBufDyn::Os(p) => Strand::Os(p.as_os_str()),
}
}
}
impl AsStrandDyn for Strand<'_> {
fn as_strand_dyn(&self) -> Strand<'_> { *self }
impl AsStrand for Strand<'_> {
fn as_strand(&self) -> Strand<'_> { *self }
}
impl AsStrandDyn for StrandBuf {
fn as_strand_dyn(&self) -> Strand<'_> { self.borrow() }
}
impl AsStrandDyn for &StrandBuf {
fn as_strand_dyn(&self) -> Strand<'_> { (*self).borrow() }
}
impl AsStrandDyn for StrandCow<'_> {
fn as_strand_dyn(&self) -> Strand<'_> {
impl AsStrand for StrandBuf {
fn as_strand(&self) -> Strand<'_> {
match self {
Self::Borrowed(s) => s.as_strand_dyn(),
Self::Owned(s) => s.as_strand_dyn(),
Self::Os(s) => Strand::Os(s),
Self::Utf8(s) => Strand::Utf8(s),
Self::Bytes(b) => Strand::Bytes(b),
}
}
}
impl AsStrand for &StrandBuf {
fn as_strand(&self) -> Strand<'_> { (**self).as_strand() }
}
impl AsStrand for StrandCow<'_> {
fn as_strand(&self) -> Strand<'_> {
match self {
StrandCow::Borrowed(s) => *s,
StrandCow::Owned(s) => s.as_strand(),
}
}
}
impl AsStrand for &StrandCow<'_> {
fn as_strand(&self) -> Strand<'_> { (**self).as_strand() }
}

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::{OsStr, OsString}};
use anyhow::Result;
use crate::{IntoOsStr, scheme::SchemeKind, strand::{Strand, StrandBuf, StrandBufLike, StrandLike}};
use crate::{IntoOsStr, scheme::SchemeKind, strand::{Strand, StrandBuf}};
// --- StrandCow
pub enum StrandCow<'a> {
@ -41,16 +41,6 @@ impl PartialEq<Strand<'_>> for StrandCow<'_> {
}
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 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(),
@ -65,11 +55,13 @@ impl<'a> StrandCow<'a> {
}
}
// FIXME: remove, instead implement StrandLike for StrandCow
pub fn encoded_bytes(&self) -> &[u8] {
match self {
Self::Borrowed(s) => s.encoded_bytes(),
Self::Owned(s) => s.encoded_bytes(),
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
}
}
}

View file

@ -1,8 +1,6 @@
use std::ffi::{OsStr, OsString};
use std::{borrow::Cow, ffi::OsStr, fmt::Display};
use serde::Serialize;
use crate::{FromWtf8, path::PathDyn, scheme::SchemeKind, strand::{AsStrandDyn, StrandBufLike, StrandError, StrandKind, StrandLike}};
use crate::{BytesExt, FromWtf8, strand::{AsStrand, StrandBuf, StrandError, StrandKind}};
// --- Strand
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
@ -44,16 +42,6 @@ impl PartialEq<&str> for Strand<'_> {
}
}
impl<'a> Strand<'a> {
pub fn to_owned(self) -> StrandBuf {
match self {
Self::Os(s) => StrandBuf::Os(s.to_owned()),
Self::Utf8(s) => StrandBuf::Utf8(s.to_owned()),
Self::Bytes(b) => StrandBuf::Bytes(b.to_owned()),
}
}
}
impl<'a> Strand<'a> {
#[inline]
pub fn as_os(self) -> Result<&'a OsStr, StrandError> {
@ -94,6 +82,38 @@ impl<'a> Strand<'a> {
unsafe { StrandBuf::from_encoded_bytes(self.kind(), out) }.into()
}
pub fn contains(self, x: impl AsStrand) -> bool {
memchr::memmem::find(self.encoded_bytes(), x.as_strand().encoded_bytes()).is_some()
}
pub fn display(self) -> impl Display {
struct D<'a>(Strand<'a>);
impl<'a> Display for D<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Strand::Os(s) => s.display().fmt(f),
Strand::Utf8(s) => s.fmt(f),
Strand::Bytes(b) => b.display().fmt(f),
}
}
}
D(self)
}
pub fn encoded_bytes(self) -> &'a [u8] {
match self {
Self::Os(s) => s.as_encoded_bytes(),
Self::Utf8(s) => s.as_bytes(),
Self::Bytes(b) => b,
}
}
pub fn eq_ignore_ascii_case(self, other: impl AsStrand) -> bool {
self.encoded_bytes().eq_ignore_ascii_case(other.as_strand().encoded_bytes())
}
#[inline]
pub unsafe fn from_encoded_bytes(kind: impl Into<StrandKind>, bytes: &'a [u8]) -> Self {
match kind.into() {
@ -102,76 +122,34 @@ impl<'a> Strand<'a> {
StrandKind::Bytes => Self::Bytes(bytes),
}
}
}
// --- StrandBuf
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(untagged)]
pub enum StrandBuf {
Os(OsString),
Utf8(String),
Bytes(Vec<u8>),
}
pub fn is_empty(self) -> bool { self.encoded_bytes().is_empty() }
impl Default for StrandBuf {
fn default() -> Self { Self::Utf8(String::new()) }
}
impl From<OsString> for StrandBuf {
fn from(value: OsString) -> Self { Self::Os(value) }
}
impl From<String> for StrandBuf {
fn from(value: String) -> Self { Self::Utf8(value) }
}
impl From<PathDyn<'_>> for StrandBuf {
fn from(value: PathDyn) -> Self {
match value {
PathDyn::Os(p) => Self::Os(p.as_os_str().to_owned()),
}
}
}
impl PartialEq<Strand<'_>> for StrandBuf {
fn eq(&self, other: &Strand<'_>) -> bool { self.borrow() == *other }
}
impl StrandBuf {
pub fn clear(&mut self) {
pub fn kind(self) -> StrandKind {
match self {
Self::Os(buf) => buf.clear(),
Self::Utf8(buf) => buf.clear(),
Self::Bytes(buf) => buf.clear(),
Self::Os(_) => StrandKind::Os,
Self::Utf8(_) => StrandKind::Utf8,
Self::Bytes(_) => StrandKind::Bytes,
}
}
pub fn try_push<T>(&mut self, s: T) -> Result<(), StrandError>
where
T: AsStrandDyn,
{
let s = s.as_strand_dyn();
Ok(match self {
Self::Os(buf) => buf.push(s.as_os()?),
Self::Utf8(buf) => buf.push_str(s.as_utf8()?),
Self::Bytes(buf) => buf.extend(s.encoded_bytes()),
})
pub fn len(self) -> usize { self.encoded_bytes().len() }
pub fn starts_with(self, needle: impl AsStrand) -> bool {
self.encoded_bytes().starts_with(needle.as_strand().encoded_bytes())
}
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 to_owned(self) -> StrandBuf {
match self {
Self::Os(s) => StrandBuf::Os(s.to_owned()),
Self::Utf8(s) => StrandBuf::Utf8(s.to_owned()),
Self::Bytes(b) => StrandBuf::Bytes(b.to_owned()),
}
}
#[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::Bytes => Self::Bytes(bytes),
}
pub fn to_str(self) -> Result<&'a str, std::str::Utf8Error> {
str::from_utf8(self.encoded_bytes())
}
pub fn to_string_lossy(self) -> Cow<'a, str> { String::from_utf8_lossy(self.encoded_bytes()) }
}

View file

@ -20,5 +20,5 @@ impl From<PathDynError> for StrandError {
}
impl From<StrandError> for std::io::Error {
fn from(err: StrandError) -> Self { std::io::Error::other(err) }
fn from(err: StrandError) -> Self { Self::other(err) }
}

View file

@ -0,0 +1,47 @@
use std::{borrow::Cow, ffi::OsStr, fmt::Display};
use crate::strand::{AsStrand, StrandBuf, StrandCow, StrandError, StrandKind};
// --- StrandLike
pub trait StrandLike: AsStrand {
fn as_os(&self) -> Result<&OsStr, StrandError> { self.as_strand().as_os() }
fn as_utf8(&self) -> Result<&str, StrandError> { self.as_strand().as_utf8() }
#[cfg(windows)]
fn backslash_to_slash(&self) -> StrandCow<'_> { self.as_strand().backslash_to_slash() }
fn contains(&self, x: impl AsStrand) -> bool { self.as_strand().contains(x) }
fn display(&self) -> impl Display { self.as_strand().display() }
fn encoded_bytes(&self) -> &[u8] { self.as_strand().encoded_bytes() }
fn eq_ignore_ascii_case(&self, other: impl AsStrand) -> bool {
self.as_strand().eq_ignore_ascii_case(other)
}
fn is_empty(&self) -> bool { self.as_strand().is_empty() }
fn kind(&self) -> StrandKind { self.as_strand().kind() }
fn len(&self) -> usize { self.as_strand().len() }
fn starts_with(&self, needle: impl AsStrand) -> bool { self.as_strand().starts_with(needle) }
fn to_owned(&self) -> StrandBuf { self.as_strand().to_owned() }
fn to_str(&self) -> Result<&str, std::str::Utf8Error> { self.as_strand().to_str() }
fn to_string_lossy(&self) -> Cow<'_, str> { self.as_strand().to_string_lossy() }
}
impl<S> From<&S> for StrandBuf
where
S: StrandLike,
{
fn from(value: &S) -> Self { value.to_owned() }
}
impl StrandLike for StrandBuf {}
impl StrandLike for StrandCow<'_> {}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(conversion cow error kind r#dyn strand view);
yazi_macro::mod_flat!(buf conversion cow error kind like r#dyn view);

View file

@ -1,168 +0,0 @@
use std::{borrow::Cow, ffi::{OsStr, OsString}, fmt::Display};
use crate::{BytesExt, strand::{AsStrand, Strand, StrandBuf, StrandKind}};
// --- StrandLike
pub trait StrandLike<'a>: Copy {
type Owned: StrandBufLike;
fn contains(self, x: impl AsStrand) -> bool {
memchr::memmem::find(self.encoded_bytes(), x.as_strand().encoded_bytes()).is_some()
}
fn display(self) -> impl Display;
fn encoded_bytes(self) -> &'a [u8];
fn is_empty(self) -> bool { self.encoded_bytes().is_empty() }
fn kind(self) -> StrandKind;
fn len(self) -> usize { self.encoded_bytes().len() }
fn to_str(self) -> Result<&'a str, std::str::Utf8Error> { str::from_utf8(self.encoded_bytes()) }
fn to_string_lossy(self) -> Cow<'a, str> { String::from_utf8_lossy(self.encoded_bytes()) }
fn eq_ignore_ascii_case(self, other: impl AsStrand) -> bool {
self.encoded_bytes().eq_ignore_ascii_case(other.as_strand().encoded_bytes())
}
fn starts_with(self, needle: impl AsStrand) -> bool {
self.encoded_bytes().starts_with(needle.as_strand().encoded_bytes())
}
}
impl<'a> StrandLike<'a> for &'a [u8] {
type Owned = Vec<u8>;
fn display(self) -> impl Display { BytesExt::display(self) }
fn encoded_bytes(self) -> &'a [u8] { self }
fn kind(self) -> StrandKind { StrandKind::Bytes }
}
impl<'a> StrandLike<'a> for &'a str {
type Owned = String;
fn display(self) -> impl Display { self }
fn encoded_bytes(self) -> &'a [u8] { self.as_bytes() }
fn kind(self) -> StrandKind { StrandKind::Utf8 }
}
impl<'a> StrandLike<'a> for &'a OsStr {
type Owned = OsString;
fn display(self) -> impl Display { self.display() }
fn encoded_bytes(self) -> &'a [u8] { self.as_encoded_bytes() }
fn kind(self) -> StrandKind { StrandKind::Os }
}
impl<'a> StrandLike<'a> for Strand<'a> {
type Owned = StrandBuf;
fn display(self) -> impl Display {
struct D<'a>(Strand<'a>);
impl<'a> Display for D<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Strand::Os(s) => s.display().fmt(f),
Strand::Utf8(s) => s.fmt(f),
Strand::Bytes(b) => BytesExt::display(b).fmt(f),
}
}
}
D(self)
}
fn encoded_bytes(self) -> &'a [u8] {
match self {
Self::Os(s) => s.as_encoded_bytes(),
Self::Utf8(s) => s.as_bytes(),
Self::Bytes(b) => b,
}
}
fn kind(self) -> StrandKind {
match self {
Self::Os(_) => StrandKind::Os,
Self::Utf8(_) => StrandKind::Utf8,
Self::Bytes(_) => StrandKind::Bytes,
}
}
}
// --- StrandBufLike
pub trait StrandBufLike
where
Self: 'static + AsStrand,
{
type Borrowed<'a>: StrandLike<'a>;
fn borrow(&self) -> Self::Borrowed<'_>;
fn encoded_bytes(&self) -> &[u8] { self.borrow().encoded_bytes() }
fn into_encoded_bytes(self) -> Vec<u8>;
fn is_empty(&self) -> bool { self.borrow().is_empty() }
fn kind(&self) -> StrandKind { self.borrow().kind() }
fn len(&self) -> usize { self.borrow().len() }
fn to_str(&self) -> Result<&str, std::str::Utf8Error> { self.borrow().to_str() }
fn to_string_lossy(&self) -> Cow<'_, str> { self.borrow().to_string_lossy() }
}
impl StrandBufLike for Vec<u8> {
type Borrowed<'a> = &'a [u8];
fn borrow(&self) -> Self::Borrowed<'_> { self.as_slice() }
fn into_encoded_bytes(self) -> Vec<u8> { self }
}
impl StrandBufLike for String {
type Borrowed<'a> = &'a str;
fn borrow(&self) -> Self::Borrowed<'_> { self.as_str() }
fn into_encoded_bytes(self) -> Vec<u8> { self.into_bytes() }
}
impl StrandBufLike for OsString {
type Borrowed<'a> = &'a OsStr;
fn borrow(&self) -> Self::Borrowed<'_> { self.as_os_str() }
fn into_encoded_bytes(self) -> Vec<u8> { self.into_encoded_bytes() }
}
impl StrandBufLike for StrandBuf {
type Borrowed<'a> = Strand<'a>;
fn borrow(&self) -> Self::Borrowed<'_> {
match self {
Self::Os(s) => Strand::Os(s.as_os_str()),
Self::Utf8(s) => Strand::Utf8(s.as_str()),
Self::Bytes(b) => Strand::Bytes(b),
}
}
fn into_encoded_bytes(self) -> Vec<u8> {
match self {
Self::Os(s) => s.into_encoded_bytes(),
Self::Utf8(s) => s.into_bytes(),
Self::Bytes(b) => b,
}
}
}

View file

@ -1,6 +1,6 @@
use std::ffi::OsStr;
use crate::{path::PathDyn, strand::{AsStrandDyn, Strand}};
use crate::{path::PathDyn, strand::Strand};
// --- AsStrandView
pub trait AsStrandView<'a, T> {
@ -42,10 +42,3 @@ impl<'a> AsStrandView<'a, Strand<'a>> for PathDyn<'a> {
}
}
}
impl<'a, T> AsStrandView<'a, Strand<'a>> for &'a T
where
T: AsStrandDyn,
{
fn as_strand_view(self) -> Strand<'a> { self.as_strand_dyn() }
}

View file

@ -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, PathLike, SetNameError}, pool::{InternStr, Pool, Symbol}, scheme::SchemeKind, strand::AsStrandDyn, url::{AsUrl, Url, UrlCow, UrlLike}};
use crate::{loc::LocBuf, path::{PathBufDyn, PathBufDynError, PathDyn, PathDynError, SetNameError}, pool::{InternStr, Pool, Symbol}, scheme::SchemeKind, strand::AsStrand, url::{AsUrl, Url, UrlCow, UrlLike}};
#[derive(Clone, Eq, Hash, PartialEq)]
pub enum UrlBuf {
@ -18,8 +18,8 @@ impl Default for UrlBuf {
fn default() -> Self { Self::Regular(Default::default()) }
}
impl From<&UrlBuf> for UrlBuf {
fn from(url: &UrlBuf) -> Self { url.clone() }
impl From<&Self> for UrlBuf {
fn from(url: &Self) -> Self { url.clone() }
}
impl From<Url<'_>> for UrlBuf {
@ -110,10 +110,10 @@ impl UrlBuf {
#[inline]
pub fn into_loc(self) -> PathBufDyn {
match self {
Self::Regular(loc) => loc.into_path().into(),
Self::Search { loc, .. } => loc.into_path().into(),
Self::Archive { loc, .. } => loc.into_path().into(),
Self::Sftp { loc, .. } => loc.into_path().into(),
Self::Regular(loc) => loc.into_inner().into(),
Self::Search { loc, .. } => loc.into_inner().into(),
Self::Archive { loc, .. } => loc.into_inner().into(),
Self::Sftp { loc, .. } => loc.into_inner().into(),
}
}
@ -122,8 +122,8 @@ impl UrlBuf {
if self.kind().is_local() { self.into_loc().into_os().ok() } else { None }
}
pub fn try_set_name(&mut self, name: impl AsStrandDyn) -> Result<(), SetNameError> {
let name = name.as_strand_dyn();
pub fn try_set_name(&mut self, name: impl AsStrand) -> Result<(), SetNameError> {
let name = name.as_strand();
Ok(match self {
Self::Regular(loc) => loc.try_set_name(name.as_os()?)?,
Self::Search { loc, .. } => loc.try_set_name(name.as_os()?)?,
@ -132,19 +132,17 @@ impl UrlBuf {
})
}
pub fn rebase(&self, base: &Path) -> Result<Self> {
Ok(match self {
Self::Regular(loc) => Self::Regular(loc.rebase(base)?),
pub fn rebase(&self, base: &Path) -> Self {
match self {
Self::Regular(loc) => Self::Regular(loc.rebase(base)),
Self::Search { loc, domain } => {
Self::Search { loc: loc.rebase(base)?, domain: domain.clone() }
Self::Search { loc: loc.rebase(base), domain: domain.clone() }
}
Self::Archive { loc, domain } => {
Self::Archive { loc: loc.rebase(base)?, domain: domain.clone() }
Self::Archive { loc: loc.rebase(base), domain: domain.clone() }
}
Self::Sftp { loc, domain } => {
Self::Sftp { loc: loc.rebase(base)?, domain: domain.clone() }
Self::Sftp { loc, domain } => Self::Sftp { loc: loc.rebase(base), domain: domain.clone() },
}
})
}
}
@ -275,8 +273,10 @@ mod tests {
("archive://:1:1//a/b.zip/c", Some("archive:////a/b.zip")),
("archive:////a/b.zip", Some("/a")),
// SFTP
("sftp://remote:1:1//a/b", Some("sftp://remote//a")),
("sftp://remote:3:1//a/b", Some("sftp://remote//a")),
("sftp://remote:2:1//a", Some("sftp://remote//")),
("sftp://remote:1:1//a", Some("sftp://remote//")),
("sftp://remote//a", Some("sftp://remote//")),
("sftp://remote:1//", None),
("sftp://remote//", None),
// Relative

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::{OsStr, OsString}, iter::FusedIterator, ops::Not, pa
use anyhow::Result;
use crate::{path::{PathBufDyn, PathCow, PathLike}, scheme::SchemeRef, url::{Encode, Url, UrlBuf, UrlCow}};
use crate::{path::{PathBufDyn, PathCow}, scheme::SchemeRef, url::{Encode, Url, UrlBuf, UrlCow}};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Component<'a> {

View file

@ -1,4 +1,4 @@
use crate::{path::PathLike, scheme::Encode, url::Url};
use crate::{scheme::Encode, url::Url};
pub struct Display<'a> {
inner: Url<'a>,

View file

@ -2,7 +2,7 @@ use std::fmt::{self, Display};
use percent_encoding::{CONTROLS, percent_encode};
use crate::{path::PathLike, url::Url};
use crate::url::Url;
// --- Tilded
#[derive(Clone, Copy)]

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
use anyhow::Result;
use crate::{loc::Loc, path::{AsPathRef, EndsWithError, JoinError, PathDyn, StartsWithError, StripPrefixError}, scheme::{SchemeKind, SchemeRef}, strand::{AsStrandDyn, Strand}, url::{Components, Display, Url, UrlBuf, UrlCow}};
use crate::{loc::Loc, path::{AsPathRef, EndsWithError, JoinError, PathDyn, StartsWithError, StripPrefixError}, scheme::{SchemeKind, SchemeRef}, strand::{AsStrand, Strand}, url::{Components, Display, Url, UrlBuf, UrlCow}};
// --- AsUrl
pub trait AsUrl {
@ -103,7 +103,7 @@ where
// UrlLike
pub trait UrlLike
where
Self: AsUrl + Sized,
Self: AsUrl,
{
fn as_local(&self) -> Option<&Path> { self.as_url().as_local() }
@ -147,7 +147,7 @@ where
self.as_url().try_ends_with(child)
}
fn try_join(&self, path: impl AsStrandDyn) -> Result<UrlBuf, JoinError> {
fn try_join(&self, path: impl AsStrand) -> Result<UrlBuf, JoinError> {
self.as_url().try_join(path)
}

View file

@ -5,7 +5,7 @@ use hashbrown::Equivalent;
use serde::Serialize;
use super::Encode as EncodeUrl;
use crate::{loc::{Loc, LocBuf}, path::{AsPathDyn, AsPathRef, EndsWithError, JoinError, PathBufDyn, PathBufLike, PathDyn, PathDynError, PathLike, StartsWithError, StripPrefixError}, pool::InternStr, scheme::{Encode as EncodeScheme, SchemeCow, SchemeKind, SchemeRef}, strand::{AsStrandDyn, Strand}, url::{AsUrl, Components, UrlBuf, UrlCow}};
use crate::{loc::{Loc, LocBuf}, path::{AsPath, AsPathRef, EndsWithError, JoinError, PathBufDyn, PathDyn, PathDynError, PathLike, StartsWithError, StripPrefixError}, pool::InternStr, scheme::{Encode as EncodeScheme, SchemeCow, SchemeKind, SchemeRef}, strand::{AsStrand, Strand}, url::{AsUrl, Components, UrlBuf, UrlCow}};
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub enum Url<'a> {
@ -58,14 +58,13 @@ impl<'a> Url<'a> {
}
}
// FIXME: add to UrlLike trait
#[inline]
pub fn loc(self) -> PathDyn<'a> {
match self {
Self::Regular(loc) => loc.as_path_dyn(),
Self::Search { loc, .. } => loc.as_path_dyn(),
Self::Archive { loc, .. } => loc.as_path_dyn(),
Self::Sftp { loc, .. } => loc.as_path_dyn(),
Self::Regular(loc) => loc.as_path(),
Self::Search { loc, .. } => loc.as_path(),
Self::Archive { loc, .. } => loc.as_path(),
Self::Sftp { loc, .. } => loc.as_path(),
}
}
@ -105,8 +104,8 @@ impl<'a> Url<'a> {
#[inline]
pub fn to_owned(self) -> UrlBuf { self.into() }
pub fn try_join(self, path: impl AsStrandDyn) -> Result<UrlBuf, JoinError> {
let joined = self.loc().try_join(&path)?;
pub fn try_join(self, path: impl AsStrand) -> Result<UrlBuf, JoinError> {
let joined = self.loc().try_join(path)?;
Ok(match self {
Self::Regular(_) => UrlBuf::Regular(joined.into_os()?.into()),
@ -177,7 +176,7 @@ impl<'a> Url<'a> {
use Url as U;
let base = base.as_url();
let prefix = self.loc().try_strip_prefix(base.loc())?.into();
let prefix = self.loc().try_strip_prefix(base.loc())?;
match (self, base) {
// Same scheme
@ -221,50 +220,50 @@ impl<'a> Url<'a> {
#[inline]
pub fn uri(self) -> PathDyn<'a> {
match self {
Self::Regular(loc) => loc.uri().as_path_dyn(),
Self::Search { loc, .. } => loc.uri().as_path_dyn(),
Self::Archive { loc, .. } => loc.uri().as_path_dyn(),
Self::Sftp { loc, .. } => loc.uri().as_path_dyn(),
Self::Regular(loc) => loc.uri().as_path(),
Self::Search { loc, .. } => loc.uri().as_path(),
Self::Archive { loc, .. } => loc.uri().as_path(),
Self::Sftp { loc, .. } => loc.uri().as_path(),
}
}
#[inline]
pub fn urn(self) -> PathDyn<'a> {
match self {
Self::Regular(loc) => loc.urn().as_path_dyn(),
Self::Search { loc, .. } => loc.urn().as_path_dyn(),
Self::Archive { loc, .. } => loc.urn().as_path_dyn(),
Self::Sftp { loc, .. } => loc.urn().as_path_dyn(),
Self::Regular(loc) => loc.urn().as_path(),
Self::Search { loc, .. } => loc.urn().as_path(),
Self::Archive { loc, .. } => loc.urn().as_path(),
Self::Sftp { loc, .. } => loc.urn().as_path(),
}
}
#[inline]
pub fn name(self) -> Option<Strand<'a>> {
Some(match self {
Self::Regular(loc) => loc.name()?.as_strand_dyn(),
Self::Search { loc, .. } => loc.name()?.as_strand_dyn(),
Self::Archive { loc, .. } => loc.name()?.as_strand_dyn(),
Self::Sftp { loc, .. } => loc.name()?.as_strand_dyn(),
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.stem()?.as_strand_dyn(),
Self::Search { loc, .. } => loc.stem()?.as_strand_dyn(),
Self::Archive { loc, .. } => loc.stem()?.as_strand_dyn(),
Self::Sftp { loc, .. } => loc.stem()?.as_strand_dyn(),
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.ext()?.as_strand_dyn(),
Self::Search { loc, .. } => loc.ext()?.as_strand_dyn(),
Self::Archive { loc, .. } => loc.ext()?.as_strand_dyn(),
Self::Sftp { loc, .. } => loc.ext()?.as_strand_dyn(),
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(),
})
}
@ -291,11 +290,11 @@ impl<'a> Url<'a> {
match self {
Self::Regular(loc) | Self::Search { loc, .. } | Self::Archive { loc, .. } => {
let (base, rest, urn) = loc.triple();
(base.as_path_dyn(), rest.as_path_dyn(), urn.as_path_dyn())
(base.as_path(), rest.as_path(), urn.as_path())
}
Self::Sftp { loc, .. } => {
let (base, rest, urn) = loc.triple();
(base.as_path_dyn(), rest.as_path_dyn(), urn.as_path_dyn())
(base.as_path(), rest.as_path(), urn.as_path())
}
}
}

View file

@ -31,12 +31,12 @@ impl FromWtf8 for OsStr {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
Ok(OsStr::from_bytes(wtf8))
Ok(Self::from_bytes(wtf8))
}
#[cfg(windows)]
{
// FIXME: validate WTF-8
Ok(unsafe { OsStr::from_encoded_bytes_unchecked(wtf8) })
Ok(unsafe { Self::from_encoded_bytes_unchecked(wtf8) })
}
}
}
@ -57,12 +57,12 @@ impl FromWtf8Vec for OsString {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStringExt;
Ok(OsString::from_vec(wtf8))
Ok(Self::from_vec(wtf8))
}
#[cfg(windows)]
{
// FIXME: validate WTF-8
Ok(unsafe { OsString::from_encoded_bytes_unchecked(wtf8) })
Ok(unsafe { Self::from_encoded_bytes_unchecked(wtf8) })
}
}
}

View file

@ -8,6 +8,9 @@ description = "Yazi crate shims"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }

View file

@ -8,6 +8,9 @@ description = "Yazi terminal extensions"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }
yazi-shared = { path = "../yazi-shared", version = "25.9.15" }

View file

@ -3,7 +3,7 @@ pub struct SetBackground(pub bool, pub String);
impl crossterm::Command for SetBackground {
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
if self.1.is_empty() {
return Ok(());
Ok(())
} else if self.0 {
write!(f, "\x1b]11;{}\x1b\\", self.1)
} else {

View file

@ -8,6 +8,9 @@ description = "Yazi virtual file system"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[lints]
workspace = true
[dependencies]
yazi-config = { path = "../yazi-config", version = "25.9.15" }
yazi-fs = { path = "../yazi-fs", version = "25.9.15" }

View file

@ -3,7 +3,7 @@ use std::io;
use tokio::{select, sync::{mpsc, oneshot}};
use yazi_fs::provider::Attrs;
use yazi_macro::ok_or_not_found;
use yazi_shared::{strand::{StrandBuf, StrandBufLike, StrandLike}, url::{AsUrl, Url, UrlBuf, UrlLike}};
use yazi_shared::{strand::{StrandBuf, StrandLike}, url::{AsUrl, Url, UrlBuf, UrlLike}};
use crate::provider;
@ -91,7 +91,7 @@ fn _copy_with_progress(from: Url, to: Url, attrs: Attrs) -> mpsc::Receiver<Resul
});
tokio::spawn({
let (prog_tx, to) = (prog_tx.to_owned(), to.to_owned());
let (prog_tx, to) = (prog_tx.clone(), to.to_owned());
async move {
let mut last = 0;
let mut done = None;

View file

@ -1,5 +1,5 @@
use yazi_fs::FilesOp;
use yazi_shared::{path::PathLike, url::{UrlBuf, UrlLike}};
use yazi_shared::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.owned()].into()).emit();
Self::Deleting(p.into(), [n.into()].into()).emit();
}
}
}

View file

@ -2,7 +2,7 @@ use std::io;
use tokio::io::{AsyncWriteExt, BufReader, BufWriter};
use yazi_fs::{cha::Cha, provider::{Attrs, Provider, local::Local}};
use yazi_shared::{path::{AsPathDyn, PathBufDyn}, url::{AsUrl, UrlBuf, UrlCow}};
use yazi_shared::{path::{AsPath, PathBufDyn}, url::{AsUrl, UrlBuf, UrlCow}};
use super::{Providers, ReadDir, RwFile};
@ -199,7 +199,7 @@ where
pub async fn symlink_dir<P, U>(original: P, link: U) -> io::Result<()>
where
P: AsPathDyn,
P: AsPath,
U: AsUrl,
{
Providers::new(link.as_url()).await?.symlink_dir(original).await
@ -207,7 +207,7 @@ where
pub async fn symlink_file<P, U>(original: P, link: U) -> io::Result<()>
where
P: AsPathDyn,
P: AsPath,
U: AsUrl,
{
Providers::new(link.as_url()).await?.symlink_file(original).await

Some files were not shown because too many files have changed in this diff Show more