mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
refactor: introduce UrlBuf (#3072)
This commit is contained in:
parent
9810196565
commit
3ca33c1f84
67 changed files with 815 additions and 425 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -1079,6 +1079,7 @@ dependencies = [
|
|||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3534,6 +3535,7 @@ version = "25.6.11"
|
|||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"foldhash",
|
||||
"hashbrown",
|
||||
"mlua",
|
||||
"paste",
|
||||
"ratatui",
|
||||
|
|
@ -3634,6 +3636,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"crossterm 0.29.0",
|
||||
"futures",
|
||||
"hashbrown",
|
||||
"indexmap",
|
||||
"libc",
|
||||
"notify",
|
||||
|
|
@ -3662,6 +3665,7 @@ name = "yazi-dds"
|
|||
version = "25.6.11"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hashbrown",
|
||||
"mlua",
|
||||
"ordered-float 5.0.0",
|
||||
"parking_lot",
|
||||
|
|
@ -3773,6 +3777,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"bitflags 2.9.1",
|
||||
"crossterm 0.29.0",
|
||||
"hashbrown",
|
||||
"mlua",
|
||||
"serde",
|
||||
"tokio",
|
||||
|
|
@ -3872,6 +3877,7 @@ dependencies = [
|
|||
"crossterm 0.29.0",
|
||||
"foldhash",
|
||||
"futures",
|
||||
"hashbrown",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ordered-float 5.0.0",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ dirs = "6.0.0"
|
|||
foldhash = "0.1.5"
|
||||
futures = "0.3.31"
|
||||
globset = "0.4.16"
|
||||
hashbrown = { version = "0.15.5", features = [ "serde" ] }
|
||||
indexmap = { version = "2.10.0", features = [ "serde" ] }
|
||||
libc = "0.2.175"
|
||||
lru = "0.16.0"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir"],"language":"en","flagWords":[],"version":"0.2"}
|
||||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown"],"flagWords":[],"version":"0.2","language":"en"}
|
||||
|
|
@ -5,7 +5,7 @@ use yazi_fs::{CWD, path::expand_url, provider};
|
|||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt};
|
||||
use yazi_proxy::CmpProxy;
|
||||
use yazi_shared::{OsStrSplit, event::Data, natsort, url::{UrlBuf, UrnBuf}};
|
||||
use yazi_shared::{OsStrSplit, event::Data, natsort, url::{UrlBuf, UrlCow, UrnBuf}};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ impl Actor for Trigger {
|
|||
|
||||
impl Trigger {
|
||||
fn split_url(s: &str) -> Option<(UrlBuf, UrnBuf)> {
|
||||
let (scheme, path, ..) = UrlBuf::parse(s.as_bytes()).ok()?;
|
||||
let (scheme, path, ..) = UrlCow::parse(s.as_bytes()).ok()?;
|
||||
|
||||
if !scheme.is_virtual() && path.as_os_str() == "~" {
|
||||
return None; // We don't autocomplete a `~`, but `~/`
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ impl UserData for Tab {
|
|||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("history", |_, me, url: UrlRef| {
|
||||
me.history.get(&url).map(|f| Folder::make(None, f, me)).transpose()
|
||||
me.history.get(url.as_ref()).map(|f| Folder::make(None, f, me)).transpose()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ impl Actor for Cd {
|
|||
// Backstack
|
||||
if opt.source.big_jump() {
|
||||
if tab.current.url.is_regular() {
|
||||
tab.backstack.push(&tab.current.url);
|
||||
tab.backstack.push(tab.current.url.as_url());
|
||||
}
|
||||
if opt.target.is_regular() {
|
||||
tab.backstack.push(&opt.target);
|
||||
tab.backstack.push(opt.target.as_url());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ impl Actor for EscapeVisual {
|
|||
indices.into_iter().filter_map(|i| tab.current.files.get(i)).map(|f| &f.url).collect();
|
||||
|
||||
if !select {
|
||||
tab.selected.remove_many(&urls);
|
||||
} else if tab.selected.add_many(&urls) != urls.len() {
|
||||
tab.selected.remove_many(urls);
|
||||
} else if urls.len() != tab.selected.add_many(urls) {
|
||||
AppProxy::notify_warn(
|
||||
"Escape visual mode",
|
||||
"Some files cannot be selected, due to path nesting conflict.",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ impl Actor for Follow {
|
|||
let Some(link_to) = &file.link_to else { succ!() };
|
||||
|
||||
if link_to.is_absolute() {
|
||||
act!(mgr:reveal, cx, link_to.to_owned())
|
||||
act!(mgr:reveal, cx, link_to.clone())
|
||||
} else if let Some(p) = file.url.parent_url() {
|
||||
act!(mgr:reveal, cx, clean_url(p.join(link_to)))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -15,15 +15,15 @@ impl Actor for Paste {
|
|||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
let mgr = &mut cx.core.mgr;
|
||||
let tab = &mgr.tabs[cx.tab];
|
||||
let (src, dest) = (mgr.yanked.iter().collect::<Vec<_>>(), tab.cwd());
|
||||
|
||||
let dest = tab.cwd();
|
||||
if mgr.yanked.cut {
|
||||
cx.core.tasks.file_cut(&src, dest, opt.force);
|
||||
cx.core.tasks.file_cut(&mgr.yanked, dest, opt.force);
|
||||
|
||||
mgr.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(&src));
|
||||
mgr.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(mgr.yanked.iter()));
|
||||
act!(mgr:unyank, cx)
|
||||
} else {
|
||||
succ!(cx.core.tasks.file_copy(&src, dest, opt.force, opt.follow));
|
||||
succ!(cx.core.tasks.file_copy(&mgr.yanked, dest, opt.force, opt.follow));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ impl Rename {
|
|||
provider::rename(&old, &new).await?;
|
||||
|
||||
if let Some(o) = overwritten {
|
||||
ok_or_not_found(provider::rename(p_new.join(&o), &new).await)?;
|
||||
ok_or_not_found(provider::rename(&p_new.join(&o), &new).await)?;
|
||||
FilesOp::Deleting(p_new.clone(), [UrnBuf::from(o)].into()).emit();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ impl Actor for Shell {
|
|||
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
let cwd = opt.cwd.take().unwrap_or_else(|| cx.cwd().clone());
|
||||
let cwd = opt.cwd.take().unwrap_or(cx.cwd().into()).into_owned();
|
||||
let selected = cx.tab().hovered_and_selected().cloned().collect();
|
||||
|
||||
let input = opt.interactive.then(|| {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ impl Actor for TabCreate {
|
|||
|
||||
let mut tab = Tab::default();
|
||||
let (cd, url) = if let Some(wd) = opt.wd {
|
||||
(true, wd)
|
||||
(true, wd.into_owned())
|
||||
} else if let Some(h) = cx.hovered() {
|
||||
tab.pref = cx.tab().pref.clone();
|
||||
(false, h.url.to_regular())
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use yazi_macro::{render_and, succ};
|
||||
use yazi_parser::mgr::ToggleOpt;
|
||||
use yazi_proxy::AppProxy;
|
||||
use yazi_shared::event::Data;
|
||||
use yazi_shared::{event::Data, url::UrlCow};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -15,12 +15,14 @@ impl Actor for Toggle {
|
|||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
let tab = cx.tab_mut();
|
||||
let Some(url) = opt.url.as_ref().or(tab.current.hovered().map(|h| &h.url)) else { succ!() };
|
||||
let Some(url) = opt.url.or(tab.current.hovered().map(|h| UrlCow::from(&h.url))) else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
let b = match opt.state {
|
||||
Some(true) => render_and!(tab.selected.add(url)),
|
||||
Some(false) => render_and!(tab.selected.remove(url)) | true,
|
||||
None => render_and!(tab.selected.remove(url) || tab.selected.add(url)),
|
||||
Some(true) => render_and!(tab.selected.add(&url)),
|
||||
Some(false) => render_and!(tab.selected.remove(&url)) | true,
|
||||
None => render_and!(tab.selected.remove(&url) || tab.selected.add(&url)),
|
||||
};
|
||||
|
||||
if !b {
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ impl Actor for ToggleAll {
|
|||
|
||||
let warn = match either {
|
||||
Left((removal, addition)) => {
|
||||
render!(tab.selected.remove_many(&removal) > 0);
|
||||
render!(tab.selected.add_many(&addition), > 0) != addition.len()
|
||||
render!(tab.selected.remove_many(removal) > 0);
|
||||
addition.len() != render!(tab.selected.add_many(addition), > 0)
|
||||
}
|
||||
Right((removal, addition)) => {
|
||||
render!(tab.selected.remove_many(&removal) > 0);
|
||||
|
|
|
|||
|
|
@ -85,7 +85,12 @@ impl UpdateFiles {
|
|||
|
||||
fn update_hovered(cx: &mut Ctx, op: FilesOp) -> Result<Data> {
|
||||
let (id, url) = (cx.tab().id, op.cwd());
|
||||
let folder = cx.tab_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(url));
|
||||
let (_, folder) = cx
|
||||
.tab_mut()
|
||||
.history
|
||||
.raw_entry_mut()
|
||||
.from_key(url)
|
||||
.or_insert_with(|| (url.clone(), Folder::from(url)));
|
||||
|
||||
if folder.update_pub(id, op) {
|
||||
act!(mgr:peek, cx, true)?;
|
||||
|
|
@ -101,8 +106,10 @@ impl UpdateFiles {
|
|||
|
||||
tab
|
||||
.history
|
||||
.entry(op.cwd().clone())
|
||||
.or_insert_with(|| Folder::from(op.cwd()))
|
||||
.raw_entry_mut()
|
||||
.from_key(op.cwd())
|
||||
.or_insert_with(|| (op.cwd().clone(), Folder::from(op.cwd())))
|
||||
.1
|
||||
.update_pub(tab.id, op);
|
||||
|
||||
if leave {
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ impl Actor for UpdateMimes {
|
|||
.flat_map(|(key, value)| key.into_url().zip(value.into_string()))
|
||||
.filter(|(url, mime)| cx.mgr.mimetype.by_url(url) != Some(mime))
|
||||
.fold(HashMap::new(), |mut map, (u, m)| {
|
||||
for u in linked.from_file(&u) {
|
||||
for u in linked.from_file(u.as_url()) {
|
||||
map.insert(u, m.to_string());
|
||||
}
|
||||
map.insert(u, m.into_owned());
|
||||
map.insert(u.into_owned(), m.into_owned());
|
||||
map
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use yazi_core::mgr::Yanked;
|
||||
use yazi_macro::{act, render};
|
||||
use yazi_parser::mgr::YankOpt;
|
||||
use yazi_shared::{event::Data, url::UrlCov};
|
||||
use yazi_shared::{event::Data, url::UrlBufCov};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ impl Actor for Yank {
|
|||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
cx.mgr.yanked =
|
||||
Yanked::new(opt.cut, cx.tab().selected_or_hovered().cloned().map(UrlCov).collect());
|
||||
Yanked::new(opt.cut, cx.tab().selected_or_hovered().cloned().map(UrlBufCov).collect());
|
||||
render!(cx.mgr.yanked.catchup_revision(true));
|
||||
|
||||
act!(mgr:escape_select, cx)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ impl Image {
|
|||
limits.max_image_height = Some(YAZI.tasks.image_bound[1] as u32);
|
||||
}
|
||||
|
||||
let mut reader = ImageReader::new(provider::open(&url).await?.reader_sync().await);
|
||||
let mut reader = ImageReader::new(provider::open(url).await?.reader_sync().await);
|
||||
if let Ok(format) = ImageFormat::from_path(url) {
|
||||
reader.set_format(format);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.6.11" }
|
|||
# External dependencies
|
||||
ansi-to-tui = { workspace = true }
|
||||
foldhash = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
ratatui = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use yazi_shared::url::UrlCov;
|
||||
use yazi_shared::url::{UrlBufCov, UrlCow};
|
||||
|
||||
use crate::{Urn, cached_field, deprecate};
|
||||
|
||||
|
|
@ -33,23 +33,23 @@ impl From<Url> for yazi_shared::url::UrlBuf {
|
|||
fn from(value: Url) -> Self { value.inner }
|
||||
}
|
||||
|
||||
impl From<Url> for Cow<'_, yazi_shared::url::UrlBuf> {
|
||||
fn from(value: Url) -> Self { Cow::Owned(value.inner) }
|
||||
impl<'a> From<&'a Url> for yazi_shared::url::Url<'a> {
|
||||
fn from(value: &'a Url) -> Self { value.as_url() }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Url> for Cow<'a, yazi_shared::url::UrlBuf> {
|
||||
fn from(value: &'a Url) -> Self { Cow::Borrowed(&value.inner) }
|
||||
impl<'a> From<&'a Url> for UrlCow<'a> {
|
||||
fn from(value: &'a Url) -> Self { UrlCow::Borrowed(value.as_url()) }
|
||||
}
|
||||
|
||||
impl From<Url> for yazi_shared::url::UrlCov {
|
||||
fn from(value: Url) -> Self { UrlCov(value.inner) }
|
||||
impl From<Url> for yazi_shared::url::UrlBufCov {
|
||||
fn from(value: Url) -> Self { UrlBufCov(value.inner) }
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Url {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: &[u8]) -> mlua::Result<Self> {
|
||||
Ok(Self::new(yazi_shared::url::UrlBuf::try_from(value)?))
|
||||
Ok(Self::new(UrlCow::try_from(value)?.into_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ impl Boot {
|
|||
return (vec![CWD.load().as_ref().clone()], vec![UrnBuf::default()]);
|
||||
}
|
||||
|
||||
async fn go<'a>(entry: UrlBuf) -> (UrlBuf, UrnBuf) {
|
||||
async fn go(entry: UrlBuf) -> (UrlBuf, UrnBuf) {
|
||||
let Some((parent, child)) = entry.pair() else {
|
||||
return (entry, UrnBuf::default());
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::{Context, Result, bail};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::{Xdg, path::expand_url};
|
||||
use yazi_shared::{SStr, timestamp_us, url::UrlBuf};
|
||||
use yazi_shared::{SStr, timestamp_us, url::Url};
|
||||
|
||||
use super::PreviewWrap;
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ impl Preview {
|
|||
|
||||
self.cache_dir = if self.cache_dir.as_os_str().is_empty() {
|
||||
Xdg::cache_dir()
|
||||
} else if let Some(p) = expand_url(UrlBuf::from(&self.cache_dir)).into_path() {
|
||||
} else if let Some(p) = expand_url(Url::regular(&self.cache_dir)).into_path() {
|
||||
p
|
||||
} else {
|
||||
bail!("[preview].cache_dir must be a path within local filesystem.");
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ yazi-widgets = { path = "../yazi-widgets", version = "25.6.11" }
|
|||
anyhow = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
notify = { version = "8.2.0", default-features = false, features = [ "macos_fsevent" ] }
|
||||
parking_lot = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::HashMap, iter, ops::{Deref, DerefMut}};
|
||||
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{Url, UrlBuf};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Linked(HashMap<UrlBuf, UrlBuf> /* from ==> to */);
|
||||
|
|
@ -29,7 +29,7 @@ impl Linked {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_file(&self, url: &UrlBuf) -> Vec<UrlBuf> {
|
||||
pub fn from_file(&self, url: Url) -> Vec<UrlBuf> {
|
||||
if url.scheme.is_virtual() {
|
||||
vec![]
|
||||
} else if let Some((parent, urn)) = url.pair() {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
use std::{borrow::Cow, collections::HashMap};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::{MIME_DIR, SStr, url::{UrlBuf, UrlCov}};
|
||||
use yazi_shared::{MIME_DIR, SStr, url::{Url, UrlBuf, UrlBufCov, UrlCov}};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mimetype(HashMap<UrlCov, String>);
|
||||
pub struct Mimetype(HashMap<UrlBufCov, String>);
|
||||
|
||||
impl Mimetype {
|
||||
#[inline]
|
||||
pub fn by_url(&self, url: &UrlBuf) -> Option<&str> {
|
||||
self.0.get(UrlCov::new(url)).map(|s| s.as_str())
|
||||
pub fn by_url<'a>(&self, url: impl Into<Url<'a>>) -> Option<&str> {
|
||||
self.0.get(&UrlCov::new(url)).map(|s| s.as_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn by_url_owned(&self, url: &UrlBuf) -> Option<SStr> {
|
||||
pub fn by_url_owned<'a>(&self, url: impl Into<Url<'a>>) -> Option<SStr> {
|
||||
self.by_url(url).map(|s| Cow::Owned(s.to_owned()))
|
||||
}
|
||||
|
||||
|
|
@ -28,10 +29,12 @@ impl Mimetype {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, url: &UrlBuf) -> bool { self.0.contains_key(UrlCov::new(url)) }
|
||||
pub fn contains<'a>(&self, url: impl Into<Url<'a>>) -> bool {
|
||||
self.0.contains_key(&UrlCov::new(url))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn extend(&mut self, iter: impl IntoIterator<Item = (UrlBuf, String)>) {
|
||||
self.0.extend(iter.into_iter().map(|(u, m)| (UrlCov(u), m)));
|
||||
self.0.extend(iter.into_iter().map(|(u, m)| (UrlBufCov(u), m)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,33 @@
|
|||
use std::{collections::HashSet, ops::Deref};
|
||||
use std::ops::Deref;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_macro::err;
|
||||
use yazi_shared::url::{UrlBuf, UrlCov};
|
||||
use yazi_shared::url::{Url, UrlBuf, UrlBufCov, UrlCov};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Yanked {
|
||||
pub cut: bool,
|
||||
urls: HashSet<UrlCov>,
|
||||
urls: HashSet<UrlBufCov>,
|
||||
|
||||
version: u64,
|
||||
revision: u64,
|
||||
}
|
||||
|
||||
impl Deref for Yanked {
|
||||
type Target = HashSet<UrlCov>;
|
||||
type Target = HashSet<UrlBufCov>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.urls }
|
||||
}
|
||||
|
||||
impl Yanked {
|
||||
pub fn new(cut: bool, urls: HashSet<UrlCov>) -> Self { Self { cut, urls, ..Default::default() } }
|
||||
pub fn new(cut: bool, urls: HashSet<UrlBufCov>) -> Self {
|
||||
Self { cut, urls, ..Default::default() }
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, url: &UrlBuf) {
|
||||
if self.urls.remove(UrlCov::new(url)) {
|
||||
pub fn remove<'a>(&mut self, url: impl Into<Url<'a>>) {
|
||||
if self.urls.remove(&UrlCov::new(url)) {
|
||||
self.revision += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +42,9 @@ impl Yanked {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, url: impl AsRef<UrlBuf>) -> bool { self.urls.contains(UrlCov::new(&url)) }
|
||||
pub fn contains<'a>(&self, url: impl Into<Url<'a>>) -> bool {
|
||||
self.urls.contains(&UrlCov::new(url))
|
||||
}
|
||||
|
||||
pub fn contains_in(&self, dir: &UrlBuf) -> bool {
|
||||
self.urls.iter().any(|u| {
|
||||
|
|
@ -60,7 +65,7 @@ impl Yanked {
|
|||
|
||||
if !addition.is_empty() {
|
||||
let old = self.urls.len();
|
||||
self.urls.extend(addition.into_iter().map(UrlCov));
|
||||
self.urls.extend(addition.into_iter().map(UrlBufCov));
|
||||
self.revision += (old != self.urls.len()) as u64;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,31 @@
|
|||
use yazi_shared::url::{Url, UrlBuf};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Backstack<T> {
|
||||
pub struct Backstack {
|
||||
cursor: usize,
|
||||
stack: Vec<T>,
|
||||
stack: Vec<UrlBuf>,
|
||||
}
|
||||
|
||||
impl<T: Eq + Clone> Backstack<T> {
|
||||
pub fn push(&mut self, item: &T) {
|
||||
impl Backstack {
|
||||
pub fn push(&mut self, url: Url) {
|
||||
if self.stack.is_empty() {
|
||||
self.stack.push(item.clone());
|
||||
self.stack.push(url.to_owned());
|
||||
return;
|
||||
}
|
||||
|
||||
if self.stack[self.cursor] == *item {
|
||||
if self.stack[self.cursor] == url {
|
||||
return;
|
||||
}
|
||||
|
||||
self.cursor += 1;
|
||||
if self.cursor == self.stack.len() {
|
||||
self.stack.push(item.clone());
|
||||
self.stack.push(url.to_owned());
|
||||
} else {
|
||||
self.stack[self.cursor] = item.clone();
|
||||
self.stack[self.cursor] = url.to_owned();
|
||||
self.stack.truncate(self.cursor + 1);
|
||||
}
|
||||
|
||||
// Only keep 30 items before the cursor, the cleanup threshold is 60
|
||||
// Only keep 30 URLs before the cursor, the cleanup threshold is 60
|
||||
if self.stack.len() > 60 {
|
||||
let start = self.cursor.saturating_sub(30);
|
||||
self.stack.drain(..start);
|
||||
|
|
@ -31,7 +33,7 @@ impl<T: Eq + Clone> Backstack<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn shift_backward(&mut self) -> Option<&T> {
|
||||
pub fn shift_backward(&mut self) -> Option<&UrlBuf> {
|
||||
if self.cursor > 0 {
|
||||
self.cursor -= 1;
|
||||
Some(&self.stack[self.cursor])
|
||||
|
|
@ -40,7 +42,7 @@ impl<T: Eq + Clone> Backstack<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn shift_forward(&mut self) -> Option<&T> {
|
||||
pub fn shift_forward(&mut self) -> Option<&UrlBuf> {
|
||||
if self.cursor + 1 >= self.stack.len() {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -56,30 +58,30 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_backstack() {
|
||||
let mut bs: Backstack<u32> = Backstack::default();
|
||||
let mut bs: Backstack = Backstack::default();
|
||||
assert_eq!(bs.shift_forward(), None);
|
||||
|
||||
bs.push(&1);
|
||||
assert_eq!(bs.stack[bs.cursor], 1);
|
||||
bs.push(Url::regular("1"));
|
||||
assert_eq!(bs.stack[bs.cursor], Url::regular("1"));
|
||||
|
||||
bs.push(&2);
|
||||
bs.push(&3);
|
||||
assert_eq!(bs.stack[bs.cursor], 3);
|
||||
bs.push(Url::regular("2"));
|
||||
bs.push(Url::regular("3"));
|
||||
assert_eq!(bs.stack[bs.cursor], Url::regular("3"));
|
||||
|
||||
assert_eq!(bs.shift_backward(), Some(&2));
|
||||
assert_eq!(bs.shift_backward(), Some(&1));
|
||||
assert_eq!(bs.shift_backward().unwrap(), Url::regular("2"));
|
||||
assert_eq!(bs.shift_backward().unwrap(), Url::regular("1"));
|
||||
assert_eq!(bs.shift_backward(), None);
|
||||
assert_eq!(bs.shift_backward(), None);
|
||||
assert_eq!(bs.stack[bs.cursor], 1);
|
||||
assert_eq!(bs.shift_forward(), Some(&2));
|
||||
assert_eq!(bs.shift_forward(), Some(&3));
|
||||
assert_eq!(bs.stack[bs.cursor], Url::regular("1"));
|
||||
assert_eq!(bs.shift_forward().unwrap(), Url::regular("2"));
|
||||
assert_eq!(bs.shift_forward().unwrap(), Url::regular("3"));
|
||||
assert_eq!(bs.shift_forward(), None);
|
||||
|
||||
bs.shift_backward();
|
||||
bs.push(&4);
|
||||
bs.push(Url::regular("4"));
|
||||
|
||||
assert_eq!(bs.stack[bs.cursor], 4);
|
||||
assert_eq!(bs.stack[bs.cursor], Url::regular("4"));
|
||||
assert_eq!(bs.shift_forward(), None);
|
||||
assert_eq!(bs.shift_backward(), Some(&2));
|
||||
assert_eq!(bs.shift_backward().unwrap(), Url::regular("2"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ impl Default for Folder {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&UrlBuf> for Folder {
|
||||
fn from(url: &UrlBuf) -> Self { Self { url: url.clone(), ..Default::default() } }
|
||||
impl<T: Into<UrlBuf>> From<T> for Folder {
|
||||
fn from(value: T) -> Self { Self { url: value.into(), ..Default::default() } }
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{collections::HashMap, ops::{Deref, DerefMut}};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use hashbrown::HashMap;
|
||||
use yazi_shared::url::{Url, UrlBuf};
|
||||
|
||||
use super::Folder;
|
||||
|
||||
|
|
@ -21,7 +22,8 @@ impl DerefMut for History {
|
|||
|
||||
impl History {
|
||||
#[inline]
|
||||
pub fn remove_or(&mut self, url: &UrlBuf) -> Folder {
|
||||
self.0.remove(url).unwrap_or_else(|| Folder::from(url))
|
||||
pub fn remove_or<'a>(&mut self, url: impl Into<Url<'a>>) -> Folder {
|
||||
let url = url.into();
|
||||
self.0.remove(&url).unwrap_or_else(|| Folder::from(url))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use std::{collections::HashMap, ops::Deref};
|
||||
use std::ops::Deref;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use indexmap::IndexMap;
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_shared::{timestamp_us, url::{UrlBuf, UrlCov}};
|
||||
use yazi_shared::{timestamp_us, url::{Url, UrlBuf, UrlBufCov, UrlCov}};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Selected {
|
||||
inner: IndexMap<UrlCov, u64>,
|
||||
parents: HashMap<UrlCov, usize>,
|
||||
inner: IndexMap<UrlBufCov, u64>,
|
||||
parents: HashMap<UrlBufCov, usize>,
|
||||
}
|
||||
|
||||
impl Selected {
|
||||
|
|
@ -21,27 +22,36 @@ impl Selected {
|
|||
pub fn values(&self) -> impl Iterator<Item = &UrlBuf> { self.inner.keys().map(Deref::deref) }
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, url: impl AsRef<UrlBuf>) -> bool {
|
||||
self.inner.contains_key(UrlCov::new(&url))
|
||||
pub fn contains<'a>(&self, url: impl Into<Url<'a>>) -> bool {
|
||||
self.inner.contains_key(&UrlCov::new(url))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add(&mut self, url: &UrlBuf) -> bool { self.add_same(&[url]) == 1 }
|
||||
pub fn add<'a>(&mut self, url: impl Into<Url<'a>>) -> bool { self.add_same([url]) == 1 }
|
||||
|
||||
pub fn add_many(&mut self, urls: &[impl AsRef<UrlBuf>]) -> usize {
|
||||
pub fn add_many<'a, I, T>(&mut self, urls: I) -> usize
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<Url<'a>>,
|
||||
{
|
||||
let mut grouped: HashMap<_, Vec<_>> = Default::default();
|
||||
for u in urls {
|
||||
if let Some(p) = u.as_ref().parent_url() {
|
||||
for url in urls {
|
||||
let u = url.into();
|
||||
if let Some(p) = u.parent_url() {
|
||||
grouped.entry(p).or_default().push(u);
|
||||
}
|
||||
}
|
||||
grouped.into_values().map(|v| self.add_same(&v)).sum()
|
||||
grouped.into_values().map(|v| self.add_same(v)).sum()
|
||||
}
|
||||
|
||||
fn add_same(&mut self, urls: &[impl AsRef<UrlBuf>]) -> usize {
|
||||
fn add_same<'a, I, T>(&mut self, urls: I) -> usize
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<Url<'a>>,
|
||||
{
|
||||
// If it has appeared as a parent
|
||||
let urls: Vec<_> =
|
||||
urls.iter().map(UrlCov::new).filter(|&u| !self.parents.contains_key(u)).collect();
|
||||
urls.into_iter().map(UrlCov::new).filter(|u| !self.parents.contains_key(u)).collect();
|
||||
if urls.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -50,7 +60,7 @@ impl Selected {
|
|||
let mut parent = urls[0].parent_url();
|
||||
let mut parents = vec![];
|
||||
while let Some(u) = parent {
|
||||
if self.inner.contains_key(&u) {
|
||||
if self.inner.contains_key(&UrlCov::new(&u)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -59,26 +69,33 @@ impl Selected {
|
|||
}
|
||||
|
||||
let (now, len) = (timestamp_us(), self.inner.len());
|
||||
self.inner.extend(urls.iter().enumerate().map(|(i, &u)| (u.clone(), now + i as u64)));
|
||||
self.inner.extend(urls.iter().enumerate().map(|(i, u)| (u.into(), now + i as u64)));
|
||||
|
||||
for u in parents {
|
||||
*self.parents.entry(u).or_insert(0) += self.inner.len() - len;
|
||||
*self.parents.entry(UrlBufCov(u)).or_insert(0) += self.inner.len() - len;
|
||||
}
|
||||
urls.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn remove(&mut self, url: &UrlBuf) -> bool { self.remove_same(&[url]) == 1 }
|
||||
pub fn remove<'a>(&mut self, url: impl Into<Url<'a>> + Clone) -> bool {
|
||||
self.remove_same([url]) == 1
|
||||
}
|
||||
|
||||
pub fn remove_many(&mut self, urls: &[impl AsRef<UrlBuf>]) -> usize {
|
||||
pub fn remove_many<'a, I, T>(&mut self, urls: I) -> usize
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<Url<'a>>,
|
||||
{
|
||||
let mut grouped: HashMap<_, Vec<_>> = Default::default();
|
||||
for u in urls {
|
||||
if let Some(p) = u.as_ref().parent_url() {
|
||||
for url in urls {
|
||||
let u = url.into();
|
||||
if let Some(p) = u.parent_url() {
|
||||
grouped.entry(p).or_default().push(u);
|
||||
}
|
||||
}
|
||||
|
||||
let affected = grouped.into_values().map(|v| self.remove_same(&v)).sum();
|
||||
let affected = grouped.into_values().map(|v| self.remove_same(v)).sum();
|
||||
if affected > 0 {
|
||||
self.inner.sort_unstable_by(|_, a, _, b| a.cmp(b));
|
||||
}
|
||||
|
|
@ -86,19 +103,27 @@ impl Selected {
|
|||
affected
|
||||
}
|
||||
|
||||
fn remove_same(&mut self, urls: &[impl AsRef<UrlBuf>]) -> usize {
|
||||
let count = urls.iter().filter_map(|u| self.inner.swap_remove(UrlCov::new(u))).count();
|
||||
fn remove_same<'a, I, T>(&mut self, urls: I) -> usize
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<Url<'a>> + Clone,
|
||||
{
|
||||
let mut it = urls.into_iter().peekable();
|
||||
let Some(first) = it.peek().cloned().map(UrlCov::new) else { return 0 };
|
||||
|
||||
let count = it.filter_map(|u| self.inner.swap_remove(&UrlCov::new(u))).count();
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut parent = UrlCov::new(&urls[0]).parent_url();
|
||||
// FIXME: use UrlCov::parent_url() instead
|
||||
let mut parent = first.parent_url();
|
||||
while let Some(u) = parent {
|
||||
let n = self.parents.get_mut(&u).unwrap();
|
||||
let n = self.parents.get_mut(&UrlCov::new(&u)).unwrap();
|
||||
|
||||
*n -= count;
|
||||
if *n == 0 {
|
||||
self.parents.remove(&u);
|
||||
self.parents.remove(&UrlCov::new(&u));
|
||||
}
|
||||
|
||||
parent = u.parent_url();
|
||||
|
|
@ -124,9 +149,16 @@ impl Selected {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use yazi_shared::url::UrlCow;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn url(s: &str) -> UrlBuf { s.parse().unwrap() }
|
||||
fn url(s: &str) -> Url<'_> {
|
||||
match UrlCow::try_from(s).unwrap() {
|
||||
UrlCow::Borrowed(url) => url,
|
||||
UrlCow::Owned(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_non_conflicting() {
|
||||
|
|
@ -172,7 +204,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
3,
|
||||
s.add_same(&[&url("/parent/child1"), &url("/parent/child2"), &url("/parent/child3")])
|
||||
s.add_same([&url("/parent/child1"), &url("/parent/child2"), &url("/parent/child3")])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +213,7 @@ mod tests {
|
|||
let mut s = Selected::default();
|
||||
|
||||
s.add(&url("/parent"));
|
||||
assert_eq!(0, s.add_same(&[&url("/parent/child1"), &url("/parent/child2")]));
|
||||
assert_eq!(0, s.add_same([&url("/parent/child1"), &url("/parent/child2")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -189,14 +221,14 @@ mod tests {
|
|||
let mut s = Selected::default();
|
||||
|
||||
s.add(&url("/parent/child1"));
|
||||
assert_eq!(2, s.add_same(&[&url("/parent/child1"), &url("/parent/child2")]));
|
||||
assert_eq!(2, s.add_same([&url("/parent/child1"), &url("/parent/child2")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_many_empty_urls_list() {
|
||||
let mut s = Selected::default();
|
||||
|
||||
assert_eq!(0, s.add_same(&[] as &[&UrlBuf]));
|
||||
assert_eq!(0, s.add_same([] as [&Url; 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -204,14 +236,14 @@ mod tests {
|
|||
let mut s = Selected::default();
|
||||
|
||||
s.add(&url("/parent/child"));
|
||||
assert_eq!(0, s.add_same(&[&url("/parent/child/child1"), &url("/parent/child/child2")]));
|
||||
assert_eq!(0, s.add_same([&url("/parent/child/child1"), &url("/parent/child/child2")]));
|
||||
}
|
||||
#[test]
|
||||
fn insert_many_with_direct_parent_fails() {
|
||||
let mut s = Selected::default();
|
||||
|
||||
s.add(&url("/a"));
|
||||
assert_eq!(0, s.add_same(&[&url("/a/b")]));
|
||||
assert_eq!(0, s.add_same([&url("/a/b")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -219,15 +251,15 @@ mod tests {
|
|||
let mut s = Selected::default();
|
||||
|
||||
s.add(&url("/a/b"));
|
||||
assert_eq!(0, s.add_same(&[&url("/a")]));
|
||||
assert_eq!(1, s.add_same(&[&url("/b"), &url("/a")]));
|
||||
assert_eq!(0, s.add_same([&url("/a")]));
|
||||
assert_eq!(1, s.add_same([&url("/b"), &url("/a")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_many_sibling_directories_success() {
|
||||
let mut s = Selected::default();
|
||||
|
||||
assert_eq!(2, s.add_same(&[&url("/a/b"), &url("/a/c")]));
|
||||
assert_eq!(2, s.add_same([&url("/a/b"), &url("/a/c")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -235,7 +267,7 @@ mod tests {
|
|||
let mut s = Selected::default();
|
||||
|
||||
s.add(&url("/a/b"));
|
||||
assert_eq!(0, s.add_same(&[&url("/a/b/c")]));
|
||||
assert_eq!(0, s.add_same([&url("/a/b/c")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -245,7 +277,7 @@ mod tests {
|
|||
let child1 = url("/parent/child1");
|
||||
let child2 = url("/parent/child2");
|
||||
let child3 = url("/parent/child3");
|
||||
assert_eq!(3, s.add_same(&[&child1, &child2, &child3]));
|
||||
assert_eq!(3, s.add_same([&child1, &child2, &child3]));
|
||||
|
||||
assert!(s.remove(&child1));
|
||||
assert_eq!(s.inner.len(), 2);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub struct Tab {
|
|||
pub current: Folder,
|
||||
pub parent: Option<Folder>,
|
||||
|
||||
pub backstack: Backstack<UrlBuf>,
|
||||
pub backstack: Backstack,
|
||||
pub history: History,
|
||||
pub selected: Selected,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use tracing::debug;
|
||||
use yazi_shared::url::{UrlBuf, UrlCov};
|
||||
use yazi_shared::url::{UrlBuf, UrlBufCov};
|
||||
|
||||
use super::Tasks;
|
||||
use crate::mgr::Yanked;
|
||||
|
||||
impl Tasks {
|
||||
pub fn file_cut(&self, src: &[&UrlCov], dest: &UrlBuf, force: bool) {
|
||||
for &u in src {
|
||||
pub fn file_cut(&self, src: &Yanked, dest: &UrlBuf, force: bool) {
|
||||
for u in src.iter() {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
debug!("file_cut: same file, skipping {:?}", to);
|
||||
|
|
@ -17,8 +17,8 @@ impl Tasks {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn file_copy(&self, src: &[&UrlCov], dest: &UrlBuf, force: bool, follow: bool) {
|
||||
for &u in src {
|
||||
pub fn file_copy(&self, src: &Yanked, dest: &UrlBuf, force: bool, follow: bool) {
|
||||
for u in src.iter() {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
debug!("file_copy: same file, skipping {:?}", to);
|
||||
|
|
@ -28,7 +28,7 @@ impl Tasks {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn file_link(&self, src: &HashSet<UrlCov>, dest: &UrlBuf, relative: bool, force: bool) {
|
||||
pub fn file_link(&self, src: &HashSet<UrlBufCov>, dest: &UrlBuf, relative: bool, force: bool) {
|
||||
for u in src {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
|
|
@ -39,7 +39,7 @@ impl Tasks {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn file_hardlink(&self, src: &HashSet<UrlCov>, dest: &UrlBuf, force: bool, follow: bool) {
|
||||
pub fn file_hardlink(&self, src: &HashSet<UrlBufCov>, dest: &UrlBuf, force: bool, follow: bool) {
|
||||
for u in src {
|
||||
let to = dest.join(u.file_name().unwrap());
|
||||
if force && *u == to {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.6.11" }
|
|||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
ordered-float = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::{borrow::Cow, collections::HashSet};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use mlua::{IntoLua, Lua, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_parser::mgr::UpdateYankedOpt;
|
||||
use yazi_shared::url::UrlCov;
|
||||
use yazi_shared::url::UrlBufCov;
|
||||
|
||||
use super::Ember;
|
||||
|
||||
|
|
@ -11,13 +12,13 @@ use super::Ember;
|
|||
pub struct EmberYank<'a>(UpdateYankedOpt<'a>);
|
||||
|
||||
impl<'a> EmberYank<'a> {
|
||||
pub fn borrowed(cut: bool, urls: &'a HashSet<UrlCov>) -> Ember<'a> {
|
||||
pub fn borrowed(cut: bool, urls: &'a HashSet<UrlBufCov>) -> Ember<'a> {
|
||||
Self(UpdateYankedOpt { cut, urls: Cow::Borrowed(urls) }).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EmberYank<'static> {
|
||||
pub fn owned(cut: bool, _: &HashSet<UrlCov>) -> Ember<'static> {
|
||||
pub fn owned(cut: bool, _: &HashSet<UrlBufCov>) -> Ember<'static> {
|
||||
Self(UpdateYankedOpt { cut, urls: Default::default() }).into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use hashbrown::HashSet;
|
||||
use mlua::Function;
|
||||
use parking_lot::RwLock;
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_fs::FolderStage;
|
||||
use yazi_shared::{Id, RoCell, url::{UrlBuf, UrlCov}};
|
||||
use yazi_shared::{Id, RoCell, url::{UrlBuf, UrlBufCov}};
|
||||
|
||||
use crate::{Client, ID, PEERS, ember::{BodyMoveItem, Ember, EmberBulk, EmberHi}};
|
||||
|
||||
|
|
@ -157,7 +158,7 @@ impl Pubsub {
|
|||
|
||||
pub_after!(rename(tab: Id, from: &UrlBuf, to: &UrlBuf), (tab, from, to));
|
||||
|
||||
pub_after!(@yank(cut: bool, urls: &HashSet<UrlCov>), (cut, urls));
|
||||
pub_after!(@yank(cut: bool, urls: &HashSet<UrlBufCov>), (cut, urls));
|
||||
|
||||
pub_after!(move(items: Vec<BodyMoveItem>), (&items), (items));
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ impl SizeCalculator {
|
|||
return Ok(Self::Idle((VecDeque::new(), Some(meta.len()))));
|
||||
}
|
||||
|
||||
let mut buf = VecDeque::from([Either::Right(provider::read_dir_sync(u)?)]);
|
||||
let mut buf = VecDeque::from([Either::Right(provider::read_dir_sync(&u)?)]);
|
||||
let size = Self::next_chunk(&mut buf);
|
||||
Ok(Self::Idle((buf, size)))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ impl File {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_dummy(url: UrlBuf, ft: Option<FileType>) -> Self {
|
||||
pub fn from_dummy(url: impl Into<UrlBuf>, ft: Option<FileType>) -> Self {
|
||||
let url = url.into();
|
||||
let cha = Cha::from_dummy(&url, ft);
|
||||
Self { url, cha, link_to: None }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,21 +4,21 @@ use std::{borrow::Cow, collections::{HashMap, HashSet}, ffi::{OsStr, OsString},
|
|||
|
||||
use anyhow::{Result, bail};
|
||||
use tokio::{fs, io, select, sync::{mpsc, oneshot}, time};
|
||||
use yazi_shared::url::{Component, UrlBuf};
|
||||
use yazi_shared::url::{Component, Url, UrlBuf};
|
||||
|
||||
use crate::{cha::Cha, provider};
|
||||
|
||||
#[inline]
|
||||
pub async fn maybe_exists(u: impl AsRef<UrlBuf>) -> bool {
|
||||
match provider::symlink_metadata(u).await {
|
||||
pub async fn maybe_exists<'a>(url: impl Into<Url<'a>>) -> bool {
|
||||
match provider::symlink_metadata(url).await {
|
||||
Ok(_) => true,
|
||||
Err(e) => e.kind() != io::ErrorKind::NotFound,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn must_be_dir(u: impl AsRef<UrlBuf>) -> bool {
|
||||
provider::metadata(u).await.is_ok_and(|m| m.is_dir())
|
||||
pub async fn must_be_dir<'a>(url: impl Into<Url<'a>>) -> bool {
|
||||
provider::metadata(url).await.is_ok_and(|m| m.is_dir())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -267,7 +267,7 @@ pub async fn remove_dir_clean(dir: &UrlBuf) {
|
|||
if entry.file_type().await.is_ok_and(|t| t.is_dir()) {
|
||||
let url = entry.url();
|
||||
Box::pin(remove_dir_clean(&url)).await;
|
||||
provider::remove_dir(url).await.ok();
|
||||
provider::remove_dir(&url).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
use std::{borrow::Cow, path::{Path, PathBuf}};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use yazi_shared::{loc::LocBuf, url::UrlBuf};
|
||||
use yazi_shared::{loc::LocBuf, url::{UrlBuf, UrlCow}};
|
||||
|
||||
pub fn clean_url<'a>(url: impl Into<Cow<'a, UrlBuf>>) -> UrlBuf {
|
||||
let cow = url.into();
|
||||
let (path, uri, urn) = clean_path_impl(&cow.loc, cow.loc.base().count(), cow.loc.trail().count());
|
||||
pub fn clean_url<'a>(url: impl Into<UrlCow<'a>>) -> UrlBuf {
|
||||
let cow: UrlCow = url.into();
|
||||
let (path, uri, urn) =
|
||||
clean_path_impl(&cow.loc(), cow.loc().base().count(), cow.loc().trail().count());
|
||||
|
||||
let loc = LocBuf::with(path, uri, urn).expect("Failed to create Loc from cleaned path");
|
||||
match cow {
|
||||
Cow::Borrowed(u) => UrlBuf { loc, scheme: u.scheme.clone() },
|
||||
Cow::Owned(u) => UrlBuf { loc, scheme: u.scheme },
|
||||
UrlCow::Borrowed(u) => UrlBuf { loc, scheme: u.scheme.clone() },
|
||||
UrlCow::Owned(u) => UrlBuf { loc, scheme: u.scheme },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, path::{Path, PathBuf}};
|
||||
|
||||
use yazi_shared::{loc::LocBuf, url::UrlBuf};
|
||||
use yazi_shared::{loc::LocBuf, url::{Url, UrlBuf, UrlCow}};
|
||||
|
||||
use crate::{CWD, path::clean_url};
|
||||
|
||||
#[inline]
|
||||
pub fn expand_url<'a>(url: impl AsRef<UrlBuf>) -> UrlBuf {
|
||||
clean_url(expand_url_impl(url.as_ref()))
|
||||
pub fn expand_url<'a>(url: impl Into<UrlCow<'a>>) -> UrlBuf {
|
||||
clean_url(expand_url_impl(url.into().as_url()))
|
||||
}
|
||||
|
||||
fn expand_url_impl(url: &UrlBuf) -> Cow<'_, UrlBuf> {
|
||||
fn expand_url_impl<'a>(url: Url<'a>) -> UrlCow<'a> {
|
||||
let (o_base, o_rest, o_urn) = url.loc.triple();
|
||||
|
||||
let n_base = expand_variables(o_base);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, future::Future, io, path::PathBuf};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use yazi_shared::{loc::LocBuf, url::UrlBuf};
|
||||
use yazi_shared::{loc::LocBuf, url::{UrlBuf, UrlCow}};
|
||||
|
||||
use crate::provider;
|
||||
|
||||
|
|
@ -63,7 +63,14 @@ async fn _unique_name(mut url: UrlBuf, append: bool) -> io::Result<UrlBuf> {
|
|||
Ok(url)
|
||||
}
|
||||
|
||||
pub fn url_relative_to<'a>(from: &UrlBuf, to: &'a UrlBuf) -> Result<Cow<'a, UrlBuf>> {
|
||||
pub fn url_relative_to<'a>(
|
||||
from: impl Into<UrlCow<'a>>,
|
||||
to: impl Into<UrlCow<'a>>,
|
||||
) -> Result<UrlCow<'a>> {
|
||||
url_relative_to_impl(from.into(), to.into())
|
||||
}
|
||||
|
||||
pub fn url_relative_to_impl<'a>(from: UrlCow<'a>, to: UrlCow<'a>) -> Result<UrlCow<'a>> {
|
||||
use yazi_shared::url::Component::*;
|
||||
|
||||
if from.is_absolute() != to.is_absolute() {
|
||||
|
|
@ -74,8 +81,8 @@ pub fn url_relative_to<'a>(from: &UrlBuf, to: &'a UrlBuf) -> Result<Cow<'a, UrlB
|
|||
};
|
||||
}
|
||||
|
||||
if from.covariant(to) {
|
||||
return Ok(UrlBuf { loc: LocBuf::zeroed("."), scheme: to.scheme.clone() }.into());
|
||||
if from.covariant(&to) {
|
||||
return Ok(UrlBuf { loc: LocBuf::zeroed("."), scheme: to.scheme().clone() }.into());
|
||||
}
|
||||
|
||||
let (mut f_it, mut t_it) = (from.components(), to.components());
|
||||
|
|
@ -97,7 +104,7 @@ pub fn url_relative_to<'a>(from: &UrlBuf, to: &'a UrlBuf) -> Result<Cow<'a, UrlB
|
|||
let rest = t_head.into_iter().chain(t_it);
|
||||
|
||||
let buf: PathBuf = dots.chain(rest).collect();
|
||||
Ok(UrlBuf { loc: LocBuf::zeroed(buf), scheme: to.scheme.clone() }.into())
|
||||
Ok(UrlBuf { loc: LocBuf::zeroed(buf), scheme: to.scheme().clone() }.into())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
|
@ -123,8 +130,6 @@ pub fn backslash_to_slash(p: &std::path::Path) -> Cow<'_, std::path::Path> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::url_relative_to;
|
||||
|
||||
#[test]
|
||||
|
|
@ -132,7 +137,7 @@ mod tests {
|
|||
fn assert(from: &str, to: &str, ret: &str) {
|
||||
assert_eq!(
|
||||
url_relative_to(&from.parse().unwrap(), &to.parse().unwrap()).unwrap(),
|
||||
Cow::Owned(ret.parse().unwrap())
|
||||
ret.parse().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use std::io;
|
||||
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{Url, UrlBuf};
|
||||
|
||||
use crate::provider::{ReadDir, ReadDirSync, RwFile, local::Local};
|
||||
|
||||
#[inline]
|
||||
pub async fn canonicalize(url: impl AsRef<UrlBuf>) -> io::Result<UrlBuf> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn canonicalize<'a>(url: impl Into<Url<'a>>) -> io::Result<UrlBuf> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::canonicalize(path).await.map(Into::into)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -14,8 +14,8 @@ pub async fn canonicalize(url: impl AsRef<UrlBuf>) -> io::Result<UrlBuf> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn create(url: impl AsRef<UrlBuf>) -> io::Result<RwFile> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn create<'a>(url: impl Into<Url<'a>>) -> io::Result<RwFile> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::create(path).await.map(Into::into)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -23,8 +23,8 @@ pub async fn create(url: impl AsRef<UrlBuf>) -> io::Result<RwFile> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn create_dir(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn create_dir<'a>(url: impl Into<Url<'a>>) -> io::Result<()> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::create_dir(path).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -32,8 +32,8 @@ pub async fn create_dir(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn create_dir_all(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn create_dir_all<'a>(url: impl Into<Url<'a>>) -> io::Result<()> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::create_dir_all(path).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -41,8 +41,11 @@ pub async fn create_dir_all(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn hard_link(original: impl AsRef<UrlBuf>, link: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let (Some(original), Some(link)) = (original.as_ref().as_path(), link.as_ref().as_path()) {
|
||||
pub async fn hard_link<'a>(
|
||||
original: impl Into<Url<'a>>,
|
||||
link: impl Into<Url<'a>>,
|
||||
) -> io::Result<()> {
|
||||
if let (Some(original), Some(link)) = (original.into().as_path(), link.into().as_path()) {
|
||||
Local::hard_link(original, link).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -50,8 +53,8 @@ pub async fn hard_link(original: impl AsRef<UrlBuf>, link: impl AsRef<UrlBuf>) -
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn metadata(url: impl AsRef<UrlBuf>) -> io::Result<std::fs::Metadata> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn metadata<'a>(url: impl Into<Url<'a>>) -> io::Result<std::fs::Metadata> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::metadata(path).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -59,8 +62,8 @@ pub async fn metadata(url: impl AsRef<UrlBuf>) -> io::Result<std::fs::Metadata>
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn open(url: impl AsRef<UrlBuf>) -> io::Result<RwFile> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn open<'a>(url: impl Into<Url<'a>>) -> io::Result<RwFile> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::open(path).await.map(Into::into)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -68,8 +71,8 @@ pub async fn open(url: impl AsRef<UrlBuf>) -> io::Result<RwFile> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn read_dir(url: impl AsRef<UrlBuf>) -> io::Result<ReadDir> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn read_dir<'a>(url: impl Into<Url<'a>>) -> io::Result<ReadDir> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::read_dir(path).await.map(Into::into)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -77,8 +80,8 @@ pub async fn read_dir(url: impl AsRef<UrlBuf>) -> io::Result<ReadDir> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_dir_sync(url: impl AsRef<UrlBuf>) -> io::Result<ReadDirSync> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub fn read_dir_sync<'a>(url: impl Into<Url<'a>>) -> io::Result<ReadDirSync> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::read_dir_sync(path).map(Into::into)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -86,8 +89,8 @@ pub fn read_dir_sync(url: impl AsRef<UrlBuf>) -> io::Result<ReadDirSync> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn read_link(url: impl AsRef<UrlBuf>) -> io::Result<UrlBuf> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn read_link<'a>(url: impl Into<Url<'a>>) -> io::Result<UrlBuf> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::read_link(path).await.map(Into::into)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -95,8 +98,8 @@ pub async fn read_link(url: impl AsRef<UrlBuf>) -> io::Result<UrlBuf> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn remove_dir(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn remove_dir<'a>(url: impl Into<Url<'a>>) -> io::Result<()> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::remove_dir(path).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -104,8 +107,8 @@ pub async fn remove_dir(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn remove_dir_all(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn remove_dir_all<'a>(url: impl Into<Url<'a>>) -> io::Result<()> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::remove_dir_all(path).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -113,8 +116,8 @@ pub async fn remove_dir_all(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn remove_file(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn remove_file<'a>(url: impl Into<Url<'a>>) -> io::Result<()> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::remove_file(path).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -122,8 +125,8 @@ pub async fn remove_file(url: impl AsRef<UrlBuf>) -> io::Result<()> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn rename(from: impl AsRef<UrlBuf>, to: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let (Some(from), Some(to)) = (from.as_ref().as_path(), to.as_ref().as_path()) {
|
||||
pub async fn rename<'a>(from: impl Into<Url<'a>>, to: impl Into<Url<'a>>) -> io::Result<()> {
|
||||
if let (Some(from), Some(to)) = (from.into().as_path(), to.into().as_path()) {
|
||||
Local::rename(from, to).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -131,8 +134,11 @@ pub async fn rename(from: impl AsRef<UrlBuf>, to: impl AsRef<UrlBuf>) -> io::Res
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn symlink_dir(original: impl AsRef<UrlBuf>, link: impl AsRef<UrlBuf>) -> io::Result<()> {
|
||||
if let (Some(original), Some(link)) = (original.as_ref().as_path(), link.as_ref().as_path()) {
|
||||
pub async fn symlink_dir<'a>(
|
||||
original: impl Into<Url<'a>>,
|
||||
link: impl Into<Url<'a>>,
|
||||
) -> io::Result<()> {
|
||||
if let (Some(original), Some(link)) = (original.into().as_path(), link.into().as_path()) {
|
||||
Local::symlink_dir(original, link).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -140,11 +146,11 @@ pub async fn symlink_dir(original: impl AsRef<UrlBuf>, link: impl AsRef<UrlBuf>)
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn symlink_file(
|
||||
original: impl AsRef<UrlBuf>,
|
||||
link: impl AsRef<UrlBuf>,
|
||||
pub async fn symlink_file<'a>(
|
||||
original: impl Into<Url<'a>>,
|
||||
link: impl Into<Url<'a>>,
|
||||
) -> io::Result<()> {
|
||||
if let (Some(original), Some(link)) = (original.as_ref().as_path(), link.as_ref().as_path()) {
|
||||
if let (Some(original), Some(link)) = (original.into().as_path(), link.into().as_path()) {
|
||||
Local::symlink_file(original, link).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -152,16 +158,16 @@ pub async fn symlink_file(
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn symlink_metadata(url: impl AsRef<UrlBuf>) -> io::Result<std::fs::Metadata> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn symlink_metadata<'a>(url: impl Into<Url<'a>>) -> io::Result<std::fs::Metadata> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::symlink_metadata(path).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symlink_metadata_sync(url: impl AsRef<UrlBuf>) -> io::Result<std::fs::Metadata> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub fn symlink_metadata_sync<'a>(url: impl Into<Url<'a>>) -> io::Result<std::fs::Metadata> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::symlink_metadata_sync(path)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
@ -169,8 +175,8 @@ pub fn symlink_metadata_sync(url: impl AsRef<UrlBuf>) -> io::Result<std::fs::Met
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn write(url: impl AsRef<UrlBuf>, contents: impl AsRef<[u8]>) -> io::Result<()> {
|
||||
if let Some(path) = url.as_ref().as_path() {
|
||||
pub async fn write<'a>(url: impl Into<Url<'a>>, contents: impl AsRef<[u8]>) -> io::Result<()> {
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::write(path, contents).await
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.6.11" }
|
|||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_fs::path::expand_url;
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CdOpt {
|
||||
pub target: UrlBuf,
|
||||
pub target: UrlCow<'static>,
|
||||
pub interactive: bool,
|
||||
pub source: CdSource,
|
||||
}
|
||||
|
|
@ -14,16 +14,22 @@ impl From<CmdCow> for CdOpt {
|
|||
let mut target = c.take_first_url().unwrap_or_default();
|
||||
|
||||
if !c.bool("raw") {
|
||||
target = expand_url(&target);
|
||||
target = expand_url(target).into();
|
||||
}
|
||||
|
||||
Self { target, interactive: c.bool("interactive"), source: CdSource::Cd }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(UrlCow<'static>, CdSource)> for CdOpt {
|
||||
fn from((target, source): (UrlCow<'static>, CdSource)) -> Self {
|
||||
Self { target, interactive: false, source }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(UrlBuf, CdSource)> for CdOpt {
|
||||
fn from((target, source): (UrlBuf, CdSource)) -> Self {
|
||||
Self { target, interactive: false, source }
|
||||
Self::from((UrlCow::from(target), source))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{event::{CmdCow, Data}, url::UrlBuf};
|
||||
use yazi_shared::{event::{CmdCow, Data}, url::UrlCow};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PeekOpt {
|
||||
pub skip: Option<usize>,
|
||||
pub force: bool,
|
||||
pub only_if: Option<UrlBuf>,
|
||||
pub only_if: Option<UrlCow<'static>>,
|
||||
pub upper_bound: bool,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_fs::path::expand_url;
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
||||
|
||||
use crate::mgr::CdSource;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RevealOpt {
|
||||
pub target: UrlBuf,
|
||||
pub target: UrlCow<'static>,
|
||||
pub source: CdSource,
|
||||
pub no_dummy: bool,
|
||||
}
|
||||
|
|
@ -16,19 +16,33 @@ impl From<CmdCow> for RevealOpt {
|
|||
let mut target = c.take_first_url().unwrap_or_default();
|
||||
|
||||
if !c.bool("raw") {
|
||||
target = expand_url(&target);
|
||||
target = expand_url(target).into();
|
||||
}
|
||||
|
||||
Self { target, source: CdSource::Reveal, no_dummy: c.bool("no-dummy") }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UrlCow<'static>> for RevealOpt {
|
||||
fn from(target: UrlCow<'static>) -> Self {
|
||||
Self { target, source: CdSource::Reveal, no_dummy: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(UrlCow<'static>, CdSource)> for RevealOpt {
|
||||
fn from((target, source): (UrlCow<'static>, CdSource)) -> Self {
|
||||
Self { target, source, no_dummy: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UrlBuf> for RevealOpt {
|
||||
fn from(target: UrlBuf) -> Self { Self { target, source: CdSource::Reveal, no_dummy: false } }
|
||||
fn from(target: UrlBuf) -> Self { Self::from(UrlCow::from(target)) }
|
||||
}
|
||||
|
||||
impl From<(UrlBuf, CdSource)> for RevealOpt {
|
||||
fn from((target, source): (UrlBuf, CdSource)) -> Self { Self { target, source, no_dummy: false } }
|
||||
fn from((target, source): (UrlBuf, CdSource)) -> Self {
|
||||
Self::from((UrlCow::from(target), source))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for RevealOpt {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{SStr, event::{CmdCow, Data}, url::UrlBuf};
|
||||
use yazi_shared::{SStr, event::{CmdCow, Data}, url::UrlCow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ShellOpt {
|
||||
pub run: SStr,
|
||||
pub cwd: Option<UrlBuf>,
|
||||
pub cwd: Option<UrlCow<'static>>,
|
||||
|
||||
pub block: bool,
|
||||
pub orphan: bool,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_fs::path::expand_url;
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
use yazi_shared::{event::CmdCow, url::UrlCow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TabCreateOpt {
|
||||
pub wd: Option<UrlBuf>,
|
||||
pub wd: Option<UrlCow<'static>>,
|
||||
}
|
||||
|
||||
impl From<CmdCow> for TabCreateOpt {
|
||||
|
|
@ -14,10 +14,10 @@ impl From<CmdCow> for TabCreateOpt {
|
|||
return Self { wd: None };
|
||||
}
|
||||
let Some(mut wd) = c.take_first_url() else {
|
||||
return Self { wd: Some(BOOT.cwds[0].clone()) };
|
||||
return Self { wd: Some(UrlCow::from(&BOOT.cwds[0])) };
|
||||
};
|
||||
if !c.bool("raw") {
|
||||
wd = expand_url(&wd);
|
||||
wd = expand_url(wd).into();
|
||||
}
|
||||
Self { wd: Some(wd) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
use yazi_shared::{event::CmdCow, url::UrlCow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ToggleOpt {
|
||||
pub url: Option<UrlBuf>,
|
||||
pub url: Option<UrlCow<'static>>,
|
||||
pub state: Option<bool>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
use yazi_shared::{event::CmdCow, url::UrlCow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ToggleAllOpt {
|
||||
pub urls: Vec<UrlBuf>,
|
||||
pub urls: Vec<UrlCow<'static>>,
|
||||
pub state: Option<bool>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{event::{CmdCow, Data}, url::UrlBuf};
|
||||
use yazi_shared::{event::{CmdCow, Data}, url::UrlCow};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct UpdatePagedOpt {
|
||||
pub page: Option<usize>,
|
||||
pub only_if: Option<UrlBuf>,
|
||||
pub only_if: Option<UrlCow<'static>>,
|
||||
}
|
||||
|
||||
impl From<CmdCow> for UpdatePagedOpt {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
use std::{borrow::Cow, collections::HashSet};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use anyhow::bail;
|
||||
use hashbrown::HashSet;
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_binding::get_metatable;
|
||||
use yazi_shared::{event::CmdCow, url::UrlCov};
|
||||
use yazi_shared::{event::CmdCow, url::UrlBufCov};
|
||||
|
||||
type Iter = yazi_binding::Iter<
|
||||
std::iter::Map<std::collections::hash_set::IntoIter<UrlCov>, fn(UrlCov) -> yazi_binding::Url>,
|
||||
std::iter::Map<hashbrown::hash_set::IntoIter<UrlBufCov>, fn(UrlBufCov) -> yazi_binding::Url>,
|
||||
yazi_binding::Url,
|
||||
>;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct UpdateYankedOpt<'a> {
|
||||
pub cut: bool,
|
||||
pub urls: Cow<'a, HashSet<UrlCov>>,
|
||||
pub urls: Cow<'a, HashSet<UrlBufCov>>,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for UpdateYankedOpt<'_> {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, Table, Value};
|
|||
use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, Url, UrlRef};
|
||||
use yazi_config::Pattern;
|
||||
use yazi_fs::{mounts::PARTITIONS, provider, remove_dir_clean};
|
||||
use yazi_shared::url::UrlCow;
|
||||
|
||||
use crate::bindings::SizeCalculator;
|
||||
|
||||
|
|
@ -157,7 +158,7 @@ fn expand_url(lua: &Lua) -> mlua::Result<Function> {
|
|||
lua.create_function(|_, value: Value| {
|
||||
use yazi_fs::path::expand_url;
|
||||
Ok(Url::new(match value {
|
||||
Value::String(s) => expand_url(Url::try_from(s.as_bytes().as_ref())?),
|
||||
Value::String(s) => expand_url(UrlCow::try_from(s.as_bytes().as_ref())?),
|
||||
Value::UserData(ud) => expand_url(&*ud.borrow::<yazi_binding::Url>()?),
|
||||
_ => Err("must be a string or a Url".into_lua_err())?,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::{borrow::Cow, collections::VecDeque};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use tokio::{io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
|
||||
use tracing::warn;
|
||||
use yazi_config::YAZI;
|
||||
use yazi_fs::{SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path::{skip_url, url_relative_to}, provider::{self, DirEntry}};
|
||||
use yazi_shared::{Id, url::UrlBuf};
|
||||
use yazi_shared::{Id, url::{UrlBuf, UrlCow}};
|
||||
|
||||
use super::{FileIn, FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
|
||||
use crate::{LOW, NORMAL, TaskOp, TaskProg};
|
||||
|
|
@ -63,7 +63,7 @@ impl File {
|
|||
|
||||
let src = if task.resolve {
|
||||
match provider::read_link(&task.from).await {
|
||||
Ok(u) => Cow::Owned(u),
|
||||
Ok(u) => UrlCow::from(u),
|
||||
Err(e) if e.kind() == NotFound => {
|
||||
warn!("Link task partially done: {task:?}");
|
||||
return Ok(self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?);
|
||||
|
|
@ -71,20 +71,20 @@ impl File {
|
|||
Err(e) => Err(e)?,
|
||||
}
|
||||
} else {
|
||||
Cow::Borrowed(&task.from)
|
||||
UrlCow::from(&task.from)
|
||||
};
|
||||
|
||||
let src = if task.relative {
|
||||
url_relative_to(&provider::canonicalize(task.to.parent_url().unwrap()).await?, &src)?
|
||||
url_relative_to(provider::canonicalize(&task.to.parent_url().unwrap()).await?, src)?
|
||||
} else {
|
||||
src
|
||||
};
|
||||
|
||||
ok_or_not_found(provider::remove_file(&task.to).await)?;
|
||||
if cha.is_dir() {
|
||||
provider::symlink_dir(src, &task.to).await?;
|
||||
provider::symlink_dir(&src, &task.to).await?;
|
||||
} else {
|
||||
provider::symlink_file(src, &task.to).await?;
|
||||
provider::symlink_file(&src, &task.to).await?;
|
||||
}
|
||||
|
||||
if task.delete {
|
||||
|
|
@ -95,15 +95,15 @@ impl File {
|
|||
FileIn::Hardlink(task) => {
|
||||
let cha = task.cha.unwrap();
|
||||
let src = if !task.follow {
|
||||
Cow::Borrowed(&task.from)
|
||||
UrlCow::from(&task.from)
|
||||
} else if let Ok(p) = provider::canonicalize(&task.from).await {
|
||||
Cow::Owned(p)
|
||||
UrlCow::from(p)
|
||||
} else {
|
||||
Cow::Borrowed(&task.from)
|
||||
UrlCow::from(&task.from)
|
||||
};
|
||||
|
||||
ok_or_not_found(provider::remove_file(&task.to).await)?;
|
||||
match provider::hard_link(src, &task.to).await {
|
||||
match provider::hard_link(&src, &task.to).await {
|
||||
Err(e) if e.kind() == NotFound => {
|
||||
warn!("Hardlink task partially done: {task:?}");
|
||||
}
|
||||
|
|
@ -291,7 +291,7 @@ impl File {
|
|||
|
||||
let mut dirs = VecDeque::from([task.target]);
|
||||
while let Some(target) = dirs.pop_front() {
|
||||
let Ok(mut it) = provider::read_dir(target).await else { continue };
|
||||
let Ok(mut it) = provider::read_dir(&target).await else { continue };
|
||||
|
||||
while let Ok(Some(entry)) = it.next_entry().await {
|
||||
let Ok(meta) = entry.metadata().await else { continue };
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ anyhow = { workspace = true }
|
|||
crossterm = { workspace = true }
|
||||
foldhash = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
memchr = "2.7.5"
|
||||
ordered-float = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::{Result, anyhow, bail};
|
|||
use serde::{Deserialize, de};
|
||||
|
||||
use super::{Data, DataKey};
|
||||
use crate::{Id, Layer, SStr, Source, url::UrlBuf};
|
||||
use crate::{Id, Layer, SStr, Source, url::UrlCow};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Cmd {
|
||||
|
|
@ -99,6 +99,9 @@ impl Cmd {
|
|||
#[inline]
|
||||
pub fn id(&self, name: impl Into<DataKey>) -> Option<Id> { self.get(name)?.as_id() }
|
||||
|
||||
#[inline]
|
||||
pub fn url(&self, name: impl Into<DataKey>) -> Option<UrlCow<'_>> { self.get(name)?.to_url() }
|
||||
|
||||
#[inline]
|
||||
pub fn first(&self) -> Option<&Data> { self.get(0) }
|
||||
|
||||
|
|
@ -131,7 +134,7 @@ impl Cmd {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_first_url(&mut self) -> Option<UrlBuf> { self.take_first()?.into_url() }
|
||||
pub fn take_first_url(&mut self) -> Option<UrlCow<'static>> { self.take_first()?.into_url() }
|
||||
|
||||
#[inline]
|
||||
pub fn take_any<T: 'static>(&mut self, name: impl Into<DataKey>) -> Option<T> {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, ops::Deref};
|
|||
use anyhow::Result;
|
||||
|
||||
use super::{Cmd, Data, DataKey};
|
||||
use crate::{SStr, url::UrlBuf};
|
||||
use crate::{SStr, url::UrlCow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CmdCow {
|
||||
|
|
@ -48,10 +48,10 @@ impl CmdCow {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_url(&mut self, name: impl Into<DataKey>) -> Option<UrlBuf> {
|
||||
pub fn take_url(&mut self, name: impl Into<DataKey>) -> Option<UrlCow<'static>> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take(name).and_then(Data::into_url),
|
||||
Self::Borrowed(c) => c.get(name).and_then(Data::to_url),
|
||||
Self::Borrowed(c) => c.url(name),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ impl CmdCow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn take_first_url(&mut self) -> Option<UrlBuf> {
|
||||
pub fn take_first_url(&mut self) -> Option<UrlCow<'static>> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take_first_url(),
|
||||
Self::Borrowed(c) => c.first().and_then(Data::to_url),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::{Result, bail};
|
|||
use ordered_float::OrderedFloat;
|
||||
use serde::{Deserialize, Serialize, de};
|
||||
|
||||
use crate::{Id, SStr, url::{UrlBuf, UrnBuf}};
|
||||
use crate::{Id, SStr, url::{UrlBuf, UrlCow, UrnBuf}};
|
||||
|
||||
// --- Data
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
|
@ -64,11 +64,11 @@ impl Data {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_url(self) -> Option<UrlBuf> {
|
||||
pub fn into_url(self) -> Option<UrlCow<'static>> {
|
||||
match self {
|
||||
Self::String(s) => s.parse().ok(),
|
||||
Self::Url(u) => Some(u),
|
||||
Self::Bytes(b) => b.as_slice().try_into().ok(),
|
||||
Self::String(s) => s.try_into().ok(),
|
||||
Self::Url(u) => Some(u.into()),
|
||||
Self::Bytes(b) => b.try_into().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -94,10 +94,10 @@ impl Data {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_url(&self) -> Option<UrlBuf> {
|
||||
pub fn to_url(&self) -> Option<UrlCow<'_>> {
|
||||
match self {
|
||||
Self::String(s) => s.parse().ok(),
|
||||
Self::Url(u) => Some(u.clone()),
|
||||
Self::String(s) => s.as_ref().try_into().ok(),
|
||||
Self::Url(u) => Some(u.into()),
|
||||
Self::Bytes(b) => b.as_slice().try_into().ok(),
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -184,11 +184,11 @@ impl DataKey {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_url(self) -> Option<UrlBuf> {
|
||||
pub fn into_url(self) -> Option<UrlCow<'static>> {
|
||||
match self {
|
||||
Self::String(s) => s.parse().ok(),
|
||||
Self::Url(u) => Some(u),
|
||||
Self::Bytes(b) => b.as_slice().try_into().ok(),
|
||||
Self::String(s) => s.try_into().ok(),
|
||||
Self::Url(u) => Some(u.into()),
|
||||
Self::Bytes(b) => b.try_into().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,9 @@ impl PartialOrd for LocBuf {
|
|||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
// --- Hash
|
||||
impl Hash for LocBuf {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.inner.hash(state) }
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.as_loc().hash(state) }
|
||||
}
|
||||
|
||||
impl Debug for LocBuf {
|
||||
|
|
@ -137,23 +138,6 @@ impl LocBuf {
|
|||
loc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn triple(&self) -> (&Path, &Path, &Path) {
|
||||
let len = self.bytes().len();
|
||||
|
||||
let base = ..len - self.uri;
|
||||
let rest = len - self.uri..len - self.urn;
|
||||
let urn = len - self.urn..;
|
||||
|
||||
unsafe {
|
||||
(
|
||||
Path::new(OsStr::from_encoded_bytes_unchecked(self.bytes().get_unchecked(base))),
|
||||
Path::new(OsStr::from_encoded_bytes_unchecked(self.bytes().get_unchecked(rest))),
|
||||
Path::new(OsStr::from_encoded_bytes_unchecked(self.bytes().get_unchecked(urn))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bytes(&self) -> &[u8] { self.inner.as_os_str().as_encoded_bytes() }
|
||||
|
||||
|
|
@ -174,7 +158,7 @@ impl LocBuf {
|
|||
pub fn urn(&self) -> &Urn { self.as_loc().urn() }
|
||||
|
||||
#[inline]
|
||||
pub fn urn_owned(&self) -> UrnBuf { self.urn().to_owned() }
|
||||
pub fn urn_owned(&self) -> UrnBuf { self.as_loc().urn_owned() }
|
||||
|
||||
#[inline]
|
||||
pub fn base(&self) -> &Urn { self.as_loc().base() }
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::{ffi::OsStr, ops::Deref, path::Path};
|
||||
use std::{ffi::OsStr, hash::{Hash, Hasher}, ops::Deref, path::Path};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
use crate::{loc::LocBuf, url::{Uri, Urn}};
|
||||
use crate::{loc::LocBuf, url::{Uri, Urn, UrnBuf}};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Loc<'a> {
|
||||
pub(super) inner: &'a Path,
|
||||
pub(super) uri: usize,
|
||||
|
|
@ -22,6 +23,11 @@ impl<'a> From<&'a LocBuf> for Loc<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Hash
|
||||
impl Hash for Loc<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.inner.hash(state) }
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsRef<OsStr>> From<&'a T> for Loc<'a> {
|
||||
fn from(value: &'a T) -> Self {
|
||||
let path = Path::new(value.as_ref());
|
||||
|
|
@ -55,6 +61,13 @@ impl From<Loc<'_>> for LocBuf {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Eq
|
||||
impl PartialEq for Loc<'_> {
|
||||
fn eq(&self, other: &Self) -> bool { self.inner == other.inner }
|
||||
}
|
||||
|
||||
impl Eq for Loc<'_> {}
|
||||
|
||||
impl<'a> Loc<'a> {
|
||||
pub fn new<T>(path: &'a T, base: &Path, trail: &Path) -> Self
|
||||
where
|
||||
|
|
@ -98,7 +111,10 @@ impl<'a> Loc<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &'a Uri {
|
||||
pub fn as_loc(self) -> Loc<'a> { self }
|
||||
|
||||
#[inline]
|
||||
pub fn uri(self) -> &'a Uri {
|
||||
Uri::new(unsafe {
|
||||
OsStr::from_encoded_bytes_unchecked(
|
||||
self.bytes().get_unchecked(self.bytes().len() - self.uri..),
|
||||
|
|
@ -107,7 +123,7 @@ impl<'a> Loc<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn urn(&self) -> &'a Urn {
|
||||
pub fn urn(self) -> &'a Urn {
|
||||
Urn::new(unsafe {
|
||||
OsStr::from_encoded_bytes_unchecked(
|
||||
self.bytes().get_unchecked(self.bytes().len() - self.urn..),
|
||||
|
|
@ -116,7 +132,10 @@ impl<'a> Loc<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn base(&self) -> &'a Urn {
|
||||
pub fn urn_owned(self) -> UrnBuf { self.urn().to_owned() }
|
||||
|
||||
#[inline]
|
||||
pub fn base(self) -> &'a Urn {
|
||||
Urn::new(unsafe {
|
||||
OsStr::from_encoded_bytes_unchecked(
|
||||
self.bytes().get_unchecked(..self.bytes().len() - self.uri),
|
||||
|
|
@ -125,10 +144,10 @@ impl<'a> Loc<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_base(&self) -> bool { self.bytes().len() != self.uri }
|
||||
pub fn has_base(self) -> bool { self.bytes().len() != self.uri }
|
||||
|
||||
#[inline]
|
||||
pub fn trail(&self) -> &'a Urn {
|
||||
pub fn trail(self) -> &'a Urn {
|
||||
Urn::new(unsafe {
|
||||
OsStr::from_encoded_bytes_unchecked(
|
||||
self.bytes().get_unchecked(..self.bytes().len() - self.urn),
|
||||
|
|
@ -137,11 +156,28 @@ impl<'a> Loc<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_trail(&self) -> bool { self.bytes().len() != self.urn }
|
||||
pub fn has_trail(self) -> bool { self.bytes().len() != self.urn }
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> &'a OsStr { self.inner.file_name().unwrap_or(OsStr::new("")) }
|
||||
pub fn name(self) -> &'a OsStr { self.inner.file_name().unwrap_or(OsStr::new("")) }
|
||||
|
||||
#[inline]
|
||||
fn bytes(&self) -> &'a [u8] { self.inner.as_os_str().as_encoded_bytes() }
|
||||
pub fn triple(self) -> (&'a Path, &'a Path, &'a Path) {
|
||||
let len = self.bytes().len();
|
||||
|
||||
let base = ..len - self.uri;
|
||||
let rest = len - self.uri..len - self.urn;
|
||||
let urn = len - self.urn..;
|
||||
|
||||
unsafe {
|
||||
(
|
||||
Path::new(OsStr::from_encoded_bytes_unchecked(self.bytes().get_unchecked(base))),
|
||||
Path::new(OsStr::from_encoded_bytes_unchecked(self.bytes().get_unchecked(rest))),
|
||||
Path::new(OsStr::from_encoded_bytes_unchecked(self.bytes().get_unchecked(urn))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bytes(self) -> &'a [u8] { self.inner.as_os_str().as_encoded_bytes() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, hash::BuildHasher, ops::Deref, path::{Path, PathBuf}, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use percent_encoding::percent_decode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::UrnBuf;
|
||||
use crate::{IntoOsStr, loc::LocBuf, url::{Components, Display, Encode, EncodeTilded, Scheme, Url, Urn}};
|
||||
use crate::{loc::LocBuf, url::{Components, Display, Encode, EncodeTilded, Scheme, Url, UrlCow, Urn}};
|
||||
|
||||
#[derive(Clone, Default, Eq, Ord, PartialOrd, PartialEq, Hash)]
|
||||
pub struct UrlBuf {
|
||||
|
|
@ -27,6 +26,14 @@ impl From<PathBuf> for UrlBuf {
|
|||
fn from(path: PathBuf) -> Self { LocBuf::from(path).into() }
|
||||
}
|
||||
|
||||
impl From<&Url<'_>> for UrlBuf {
|
||||
fn from(url: &Url<'_>) -> Self { Self { loc: url.loc.into(), scheme: url.scheme.clone() } }
|
||||
}
|
||||
|
||||
impl From<Url<'_>> for UrlBuf {
|
||||
fn from(value: Url<'_>) -> Self { Self::from(&value) }
|
||||
}
|
||||
|
||||
impl From<&UrlBuf> for UrlBuf {
|
||||
fn from(url: &UrlBuf) -> Self { url.clone() }
|
||||
}
|
||||
|
|
@ -39,29 +46,18 @@ impl From<&Path> for UrlBuf {
|
|||
fn from(path: &Path) -> Self { path.to_path_buf().into() }
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for UrlBuf {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let (scheme, path, port) = Self::parse(bytes)?;
|
||||
|
||||
let loc =
|
||||
if let Some((uri, urn)) = port { LocBuf::with(path, uri, urn)? } else { LocBuf::from(path) };
|
||||
|
||||
Ok(Self { loc, scheme })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UrlBuf {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { s.as_bytes().try_into() }
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(UrlCow::try_from(s)?.into_owned()) }
|
||||
}
|
||||
|
||||
impl TryFrom<String> for UrlBuf {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> { value.as_bytes().try_into() }
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Ok(UrlCow::try_from(value)?.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<UrlBuf> for UrlBuf {
|
||||
|
|
@ -85,6 +81,15 @@ impl From<Cow<'_, UrlBuf>> for UrlBuf {
|
|||
fn from(url: Cow<'_, UrlBuf>) -> Self { url.into_owned() }
|
||||
}
|
||||
|
||||
// --- Eq
|
||||
impl PartialEq<Url<'_>> for UrlBuf {
|
||||
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
||||
}
|
||||
|
||||
impl PartialEq<Url<'_>> for &UrlBuf {
|
||||
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
||||
}
|
||||
|
||||
impl UrlBuf {
|
||||
pub fn join(&self, path: impl AsRef<Path>) -> Self {
|
||||
use Scheme as S;
|
||||
|
|
@ -102,12 +107,10 @@ impl UrlBuf {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> Components<'_> { Components::new(self) }
|
||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: &Self) -> bool {
|
||||
self.scheme.covariant(&other.scheme) && self.loc == other.loc
|
||||
}
|
||||
pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other.as_url()) }
|
||||
|
||||
#[inline]
|
||||
pub fn display(&self) -> Display<'_> { Display::new(self) }
|
||||
|
|
@ -115,36 +118,8 @@ impl UrlBuf {
|
|||
#[inline]
|
||||
pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||
|
||||
pub fn parent_url(&self) -> Option<UrlBuf> {
|
||||
use Scheme as S;
|
||||
|
||||
let parent = self.loc.parent()?;
|
||||
let uri = self.loc.uri();
|
||||
|
||||
Some(match self.scheme {
|
||||
// Regular
|
||||
S::Regular => Self { loc: parent.into(), scheme: S::Regular },
|
||||
|
||||
// Search
|
||||
S::Search(_) if uri.is_empty() => Self { loc: parent.into(), scheme: S::Regular },
|
||||
S::Search(_) => Self {
|
||||
loc: LocBuf::new(parent, self.loc.base(), self.loc.base()),
|
||||
scheme: self.scheme.clone(),
|
||||
},
|
||||
|
||||
// Archive
|
||||
S::Archive(_) if uri.is_empty() => Self { loc: parent.into(), scheme: S::Regular },
|
||||
S::Archive(_) if uri.nth(1).is_none() => {
|
||||
Self { loc: LocBuf::zeroed(parent), scheme: self.scheme.clone() }
|
||||
}
|
||||
S::Archive(_) => {
|
||||
Self { loc: LocBuf::floated(parent, self.loc.base()), scheme: self.scheme.clone() }
|
||||
}
|
||||
|
||||
// SFTP
|
||||
S::Sftp(_) => Self { loc: parent.into(), scheme: self.scheme.clone() },
|
||||
})
|
||||
}
|
||||
#[inline]
|
||||
pub fn parent_url(&self) -> Option<UrlBuf> { self.as_url().parent_url() }
|
||||
|
||||
pub fn strip_prefix(&self, base: impl AsRef<UrlBuf>) -> Option<&Urn> {
|
||||
use Scheme as S;
|
||||
|
|
@ -202,26 +177,6 @@ impl UrlBuf {
|
|||
|
||||
#[inline]
|
||||
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
|
||||
|
||||
pub fn parse(bytes: &[u8]) -> Result<(Scheme, PathBuf, Option<(usize, usize)>)> {
|
||||
let mut skip = 0;
|
||||
let (scheme, tilde, uri, urn) = Scheme::parse(bytes, &mut skip)?;
|
||||
|
||||
let rest = if tilde {
|
||||
Cow::from(percent_decode(&bytes[skip..])).into_os_str()?
|
||||
} else {
|
||||
bytes[skip..].into_os_str()?
|
||||
};
|
||||
|
||||
let path = match rest {
|
||||
Cow::Borrowed(s) => Path::new(s).to_owned(),
|
||||
Cow::Owned(s) => PathBuf::from(s),
|
||||
};
|
||||
|
||||
let ports = scheme.normalize_ports(uri, urn, &path)?;
|
||||
|
||||
Ok((scheme, path, ports))
|
||||
}
|
||||
}
|
||||
|
||||
impl UrlBuf {
|
||||
|
|
@ -235,7 +190,7 @@ impl UrlBuf {
|
|||
impl UrlBuf {
|
||||
// --- Regular
|
||||
#[inline]
|
||||
pub fn is_regular(&self) -> bool { self.scheme == Scheme::Regular }
|
||||
pub fn is_regular(&self) -> bool { self.as_url().is_regular() }
|
||||
|
||||
#[inline]
|
||||
pub fn to_regular(&self) -> Self {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, iter::FusedIterator, ops::Not, path::{self, PathBuf, PrefixComponent}};
|
||||
|
||||
use crate::{loc::LocBuf, url::{Encode, Scheme, UrlBuf}};
|
||||
use crate::{loc::Loc, url::{Encode, Scheme, Url, UrlBuf, UrlCow}};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Component<'a> {
|
||||
|
|
@ -60,21 +60,43 @@ impl<'a> FromIterator<Component<'a>> for PathBuf {
|
|||
#[derive(Clone)]
|
||||
pub struct Components<'a> {
|
||||
inner: path::Components<'a>,
|
||||
loc: &'a LocBuf,
|
||||
loc: Loc<'a>,
|
||||
scheme: &'a Scheme,
|
||||
scheme_yielded: bool,
|
||||
}
|
||||
|
||||
impl<'a> Components<'a> {
|
||||
pub fn new(url: &'a UrlBuf) -> Self {
|
||||
impl<'a> From<&'a Url<'a>> for Components<'a> {
|
||||
fn from(value: &'a Url<'a>) -> Self {
|
||||
Self {
|
||||
inner: url.loc.components(),
|
||||
loc: &url.loc,
|
||||
scheme: &url.scheme,
|
||||
inner: value.loc.components(),
|
||||
loc: value.loc,
|
||||
scheme: &value.scheme,
|
||||
scheme_yielded: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for Components<'a> {
|
||||
fn from(value: &'a UrlBuf) -> Self {
|
||||
Self {
|
||||
inner: value.loc.components(),
|
||||
loc: value.loc.as_loc(),
|
||||
scheme: &value.scheme,
|
||||
scheme_yielded: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlCow<'a>> for Components<'a> {
|
||||
fn from(value: &'a UrlCow<'a>) -> Self {
|
||||
match value {
|
||||
UrlCow::Borrowed(url) => Self::from(url),
|
||||
UrlCow::Owned(url) => Self::from(url),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Components<'a> {
|
||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||
let path = self.inner.as_path();
|
||||
if !self.scheme.is_virtual() || self.scheme_yielded {
|
||||
|
|
|
|||
|
|
@ -1,54 +1,87 @@
|
|||
use std::{hash::{Hash, Hasher}, ops::Deref};
|
||||
|
||||
use hashbrown::Equivalent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::url::UrlBuf;
|
||||
use crate::url::{Url, UrlBuf};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UrlCov<'a>(Url<'a>);
|
||||
|
||||
impl<'a> Deref for UrlCov<'a> {
|
||||
type Target = Url<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBufCov> for UrlCov<'a> {
|
||||
fn from(value: &'a UrlBufCov) -> Self { Self(value.0.as_url()) }
|
||||
}
|
||||
|
||||
impl PartialEq<UrlBufCov> for UrlCov<'_> {
|
||||
fn eq(&self, other: &UrlBufCov) -> bool { self.0.covariant(other.0.as_url()) }
|
||||
}
|
||||
|
||||
impl Hash for UrlCov<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.loc.hash(state);
|
||||
if self.0.scheme.is_virtual() {
|
||||
self.0.scheme.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Equivalent<UrlBufCov> for UrlCov<'_> {
|
||||
fn equivalent(&self, key: &UrlBufCov) -> bool { self == key }
|
||||
}
|
||||
|
||||
impl<'a> UrlCov<'a> {
|
||||
#[inline]
|
||||
pub fn new(url: impl Into<Url<'a>>) -> Self { Self(url.into()) }
|
||||
}
|
||||
|
||||
// --- Buf
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Serialize)]
|
||||
#[repr(transparent)]
|
||||
pub struct UrlCov(pub UrlBuf);
|
||||
pub struct UrlBufCov(pub UrlBuf);
|
||||
|
||||
impl Deref for UrlCov {
|
||||
impl Deref for UrlBufCov {
|
||||
type Target = UrlBuf;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl AsRef<UrlBuf> for UrlCov {
|
||||
fn as_ref(&self) -> &UrlBuf { &self.0 }
|
||||
}
|
||||
|
||||
impl From<UrlBuf> for UrlCov {
|
||||
impl From<UrlBuf> for UrlBufCov {
|
||||
fn from(value: UrlBuf) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl From<UrlCov> for UrlBuf {
|
||||
fn from(value: UrlCov) -> Self { value.0 }
|
||||
impl From<&UrlCov<'_>> for UrlBufCov {
|
||||
fn from(value: &UrlCov<'_>) -> Self { Self(UrlBuf::from(&value.0)) }
|
||||
}
|
||||
|
||||
impl Hash for UrlCov {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.loc.hash(state);
|
||||
if self.scheme.is_virtual() {
|
||||
self.scheme.hash(state);
|
||||
}
|
||||
}
|
||||
impl From<UrlBufCov> for UrlBuf {
|
||||
fn from(value: UrlBufCov) -> Self { value.0 }
|
||||
}
|
||||
|
||||
impl PartialEq for UrlCov {
|
||||
impl<'a> From<&'a UrlBufCov> for Url<'a> {
|
||||
fn from(value: &'a UrlBufCov) -> Self { value.0.as_url() }
|
||||
}
|
||||
|
||||
impl Hash for UrlBufCov {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.as_url().hash(state) }
|
||||
}
|
||||
|
||||
impl PartialEq for UrlBufCov {
|
||||
fn eq(&self, other: &Self) -> bool { self.covariant(other) }
|
||||
}
|
||||
|
||||
impl PartialEq<UrlBuf> for UrlCov {
|
||||
impl PartialEq<UrlBuf> for UrlBufCov {
|
||||
fn eq(&self, other: &UrlBuf) -> bool { self.covariant(other) }
|
||||
}
|
||||
|
||||
impl UrlCov {
|
||||
impl UrlBufCov {
|
||||
#[inline]
|
||||
pub fn new<T: AsRef<UrlBuf>>(u: &T) -> &Self {
|
||||
unsafe { &*(u.as_ref() as *const UrlBuf as *const Self) }
|
||||
}
|
||||
pub fn as_url(&self) -> UrlCov<'_> { UrlCov::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn parent_url(&self) -> Option<UrlCov> { self.0.parent_url().map(UrlCov) }
|
||||
pub fn parent_url(&self) -> Option<UrlBufCov> { self.0.parent_url().map(UrlBufCov) }
|
||||
}
|
||||
|
|
|
|||
159
yazi-shared/src/url/cow.rs
Normal file
159
yazi-shared/src/url/cow.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use std::{borrow::Cow, path::{Path, PathBuf}};
|
||||
|
||||
use anyhow::Result;
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use crate::{IntoOsStr, loc::{Loc, LocBuf}, url::{Components, Scheme, Url, UrlBuf, UrnBuf}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UrlCow<'a> {
|
||||
Borrowed(Url<'a>),
|
||||
Owned(UrlBuf),
|
||||
}
|
||||
|
||||
impl Default for UrlCow<'_> {
|
||||
fn default() -> Self { Self::Owned(UrlBuf::default()) }
|
||||
}
|
||||
|
||||
impl<'a> From<Url<'a>> for UrlCow<'a> {
|
||||
fn from(value: Url<'a>) -> Self { Self::Borrowed(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for UrlCow<'a> {
|
||||
fn from(value: &'a UrlBuf) -> Self { Self::Borrowed(value.into()) }
|
||||
}
|
||||
|
||||
impl From<UrlBuf> for UrlCow<'_> {
|
||||
fn from(value: UrlBuf) -> Self { Self::Owned(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlCow<'a>> for Url<'a> {
|
||||
fn from(value: &'a UrlCow<'a>) -> Self { value.as_url() }
|
||||
}
|
||||
|
||||
impl From<UrlCow<'_>> for UrlBuf {
|
||||
fn from(value: UrlCow<'_>) -> Self { value.into_owned() }
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for UrlCow<'a> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
let (scheme, path, port) = Self::parse(value)?;
|
||||
|
||||
Ok(match (path, port) {
|
||||
(Cow::Borrowed(p), None) => Url { loc: Loc::from(p), scheme }.into(),
|
||||
(Cow::Borrowed(p), Some((uri, urn))) => Url { loc: Loc::with(p, uri, urn)?, scheme }.into(),
|
||||
(Cow::Owned(p), None) => UrlBuf { loc: LocBuf::from(p), scheme }.into(),
|
||||
(Cow::Owned(p), Some((uri, urn))) => {
|
||||
UrlBuf { loc: LocBuf::with(p, uri, urn)?, scheme }.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for UrlCow<'_> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Ok(UrlCow::try_from(value.as_slice())?.into_owned().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for UrlCow<'a> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> { Self::try_from(value.as_bytes()) }
|
||||
}
|
||||
|
||||
impl TryFrom<String> for UrlCow<'_> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Ok(UrlCow::try_from(value.as_str())?.into_owned().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Cow<'a, str>> for UrlCow<'a> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Cow::Borrowed(s) => UrlCow::try_from(s),
|
||||
Cow::Owned(s) => UrlCow::try_from(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Eq
|
||||
impl PartialEq<UrlBuf> for UrlCow<'_> {
|
||||
fn eq(&self, other: &UrlBuf) -> bool { self.as_url() == other.as_url() }
|
||||
}
|
||||
|
||||
impl UrlCow<'_> {
|
||||
#[inline]
|
||||
pub fn loc(&self) -> Loc<'_> {
|
||||
match self {
|
||||
UrlCow::Borrowed(u) => u.loc.as_loc(),
|
||||
UrlCow::Owned(u) => u.loc.as_loc(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scheme(&self) -> &Scheme {
|
||||
match self {
|
||||
UrlCow::Borrowed(u) => &u.scheme,
|
||||
UrlCow::Owned(u) => &u.scheme,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_url(&self) -> Url<'_> {
|
||||
match self {
|
||||
UrlCow::Borrowed(u) => u.as_url(),
|
||||
UrlCow::Owned(u) => u.as_url(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> UrlBuf { self.as_url().into() }
|
||||
|
||||
#[inline]
|
||||
pub fn parent_url(&self) -> Option<UrlBuf> { self.as_url().parent_url() }
|
||||
|
||||
#[inline]
|
||||
pub fn pair(&self) -> Option<(UrlBuf, UrnBuf)> { self.as_url().pair() }
|
||||
|
||||
pub fn parse(bytes: &[u8]) -> Result<(Scheme, Cow<'_, Path>, Option<(usize, usize)>)> {
|
||||
let mut skip = 0;
|
||||
let (scheme, tilde, uri, urn) = Scheme::parse(bytes, &mut skip)?;
|
||||
|
||||
let rest = if tilde {
|
||||
Cow::from(percent_decode(&bytes[skip..])).into_os_str()?
|
||||
} else {
|
||||
bytes[skip..].into_os_str()?
|
||||
};
|
||||
|
||||
let path: Cow<_> = match rest {
|
||||
Cow::Borrowed(s) => Path::new(s).into(),
|
||||
Cow::Owned(s) => PathBuf::from(s).into(),
|
||||
};
|
||||
|
||||
let ports = scheme.normalize_ports(uri, urn, &path)?;
|
||||
|
||||
Ok((scheme, path, ports))
|
||||
}
|
||||
}
|
||||
|
||||
impl UrlCow<'_> {
|
||||
#[inline]
|
||||
pub fn is_regular(&self) -> bool { self.as_url().is_regular() }
|
||||
|
||||
#[inline]
|
||||
pub fn is_absolute(&self) -> bool { self.as_url().is_absolute() }
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other) }
|
||||
}
|
||||
|
|
@ -2,20 +2,24 @@ use std::{fmt::{self, Display}, ops::Not};
|
|||
|
||||
use percent_encoding::{AsciiSet, CONTROLS, PercentEncode, percent_encode};
|
||||
|
||||
use crate::{loc::LocBuf, url::{Scheme, UrlBuf}};
|
||||
use crate::{loc::Loc, url::{Scheme, Url, UrlBuf}};
|
||||
|
||||
pub struct Encode<'a> {
|
||||
loc: &'a LocBuf,
|
||||
loc: Loc<'a>,
|
||||
scheme: &'a Scheme,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Url<'a>> for Encode<'a> {
|
||||
fn from(url: &'a Url<'a>) -> Self { Self::new(url.loc, &url.scheme) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for Encode<'a> {
|
||||
fn from(url: &'a UrlBuf) -> Self { Self::new(&url.loc, &url.scheme) }
|
||||
fn from(url: &'a UrlBuf) -> Self { Self::new(url.loc.as_loc(), &url.scheme) }
|
||||
}
|
||||
|
||||
impl<'a> Encode<'a> {
|
||||
#[inline]
|
||||
pub(super) fn new(loc: &'a LocBuf, scheme: &'a Scheme) -> Self { Self { loc, scheme } }
|
||||
pub(super) fn new(loc: Loc<'a>, scheme: &'a Scheme) -> Self { Self { loc, scheme } }
|
||||
|
||||
#[inline]
|
||||
fn domain<'s>(s: &'s str) -> PercentEncode<'s> {
|
||||
|
|
@ -69,12 +73,16 @@ impl Display for Encode<'_> {
|
|||
|
||||
// --- Tilded
|
||||
pub struct EncodeTilded<'a> {
|
||||
loc: &'a LocBuf,
|
||||
loc: Loc<'a>,
|
||||
scheme: &'a Scheme,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Url<'a>> for EncodeTilded<'a> {
|
||||
fn from(url: &'a Url<'a>) -> Self { Self { loc: url.loc, scheme: &url.scheme } }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for EncodeTilded<'a> {
|
||||
fn from(url: &'a UrlBuf) -> Self { Self { loc: &url.loc, scheme: &url.scheme } }
|
||||
fn from(url: &'a UrlBuf) -> Self { Self { loc: url.loc.as_loc(), scheme: &url.scheme } }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a EncodeTilded<'a>> for Encode<'a> {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(buf component cov display encode scheme uri url urn);
|
||||
yazi_macro::mod_flat!(buf component cov cow display encode scheme uri url urn);
|
||||
|
|
|
|||
|
|
@ -1,22 +1,60 @@
|
|||
use std::path::Path;
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use crate::{loc::Loc, url::{Scheme, UrlBuf}};
|
||||
use hashbrown::Equivalent;
|
||||
|
||||
use crate::{loc::{Loc, LocBuf}, url::{Components, Scheme, UrlBuf, UrnBuf}};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Url<'a> {
|
||||
pub loc: Loc<'a>,
|
||||
pub scheme: &'a Scheme,
|
||||
pub scheme: Scheme,
|
||||
}
|
||||
|
||||
impl<'a> Deref for Url<'a> {
|
||||
type Target = Loc<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.loc }
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
impl<'a> From<&'a Url<'a>> for Url<'a> {
|
||||
fn from(value: &'a Url) -> Self { Self { loc: value.loc.as_loc(), scheme: value.scheme.clone() } }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for Url<'a> {
|
||||
fn from(value: &'a UrlBuf) -> Self { Self { loc: value.loc.as_loc(), scheme: &value.scheme } }
|
||||
fn from(value: &'a UrlBuf) -> Self {
|
||||
Self { loc: value.loc.as_loc(), scheme: value.scheme.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Url<'_>> for UrlBuf {
|
||||
fn from(value: Url<'_>) -> Self { Self { loc: value.loc.into(), scheme: value.scheme.clone() } }
|
||||
impl<'a> From<&'a mut UrlBuf> for Url<'a> {
|
||||
fn from(value: &'a mut UrlBuf) -> Self { Self::from(&*value) }
|
||||
}
|
||||
|
||||
// --- Eq
|
||||
impl PartialEq<UrlBuf> for Url<'_> {
|
||||
fn eq(&self, other: &UrlBuf) -> bool { *self == other.as_url() }
|
||||
}
|
||||
|
||||
// --- Hash
|
||||
impl Equivalent<UrlBuf> for Url<'_> {
|
||||
fn equivalent(&self, key: &UrlBuf) -> bool { self == key }
|
||||
}
|
||||
|
||||
impl<'a> Url<'a> {
|
||||
pub fn regular(path: &'a Path) -> Self { Self { loc: path.into(), scheme: &Scheme::Regular } }
|
||||
#[inline]
|
||||
pub fn regular<T: AsRef<Path> + ?Sized>(path: &'a T) -> Self {
|
||||
Self { loc: path.as_ref().into(), scheme: Scheme::Regular }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_regular(&self) -> bool { self.scheme == Scheme::Regular }
|
||||
|
||||
#[inline]
|
||||
pub fn as_url(&'a self) -> Url<'a> { Self::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn to_owned(&self) -> UrlBuf { self.into() }
|
||||
|
||||
pub fn base(&self) -> Option<Self> {
|
||||
use Scheme as S;
|
||||
|
|
@ -27,10 +65,60 @@ impl<'a> Url<'a> {
|
|||
|
||||
let loc: Loc = self.loc.base().into();
|
||||
Some(match self.scheme {
|
||||
S::Regular => Self { loc, scheme: &S::Regular },
|
||||
S::Search(_) => Self { loc, scheme: &self.scheme },
|
||||
S::Archive(_) => Self { loc, scheme: &self.scheme },
|
||||
S::Sftp(_) => Self { loc, scheme: &self.scheme },
|
||||
S::Regular => Self { loc, scheme: S::Regular },
|
||||
S::Search(_) => Self { loc, scheme: self.scheme.clone() },
|
||||
S::Archive(_) => Self { loc, scheme: self.scheme.clone() },
|
||||
S::Sftp(_) => Self { loc, scheme: self.scheme.clone() },
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: use Url instead as the return type
|
||||
pub fn parent_url(&self) -> Option<UrlBuf> {
|
||||
use Scheme as S;
|
||||
|
||||
let parent = self.loc.parent()?;
|
||||
let uri = self.loc.uri();
|
||||
|
||||
Some(match self.scheme {
|
||||
// Regular
|
||||
S::Regular => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
||||
|
||||
// Search
|
||||
S::Search(_) if uri.is_empty() => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
||||
S::Search(_) => UrlBuf {
|
||||
loc: LocBuf::new(parent, self.loc.base(), self.loc.base()),
|
||||
scheme: self.scheme.clone(),
|
||||
},
|
||||
|
||||
// Archive
|
||||
S::Archive(_) if uri.is_empty() => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
||||
S::Archive(_) if uri.nth(1).is_none() => {
|
||||
UrlBuf { loc: LocBuf::zeroed(parent), scheme: self.scheme.clone() }
|
||||
}
|
||||
S::Archive(_) => {
|
||||
UrlBuf { loc: LocBuf::floated(parent, self.loc.base()), scheme: self.scheme.clone() }
|
||||
}
|
||||
|
||||
// SFTP
|
||||
S::Sftp(_) => UrlBuf { loc: parent.into(), scheme: self.scheme.clone() },
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: impl Into<Self>) -> bool {
|
||||
let other = other.into();
|
||||
self.scheme.covariant(&other.scheme) && self.loc == other.loc
|
||||
}
|
||||
|
||||
// TODO: use Urn instead as the return type
|
||||
#[inline]
|
||||
pub fn pair(&self) -> Option<(UrlBuf, UrnBuf)> {
|
||||
Some((self.parent_url()?, self.loc.urn_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_path(&self) -> Option<&Path> { Some(&*self.loc).filter(|_| !self.scheme.is_virtual()) }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue