mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-23 07:41:03 +00:00
feat: new stash command to let users customize back and forward command behavior (#3380)
This commit is contained in:
parent
3561867b99
commit
a8f2498471
32 changed files with 702 additions and 238 deletions
|
|
@ -118,13 +118,5 @@ impl<'a> Ctx<'a> {
|
|||
pub fn tasks(&self) -> &Tasks { &self.tasks }
|
||||
|
||||
#[inline]
|
||||
pub fn source(&self) -> Source {
|
||||
use Source::*;
|
||||
match self.source {
|
||||
Key if self.level != 1 => Ind,
|
||||
Emit if self.level != 1 => EmitInd,
|
||||
Relay if self.level != 1 => RelayInd,
|
||||
s => s,
|
||||
}
|
||||
}
|
||||
pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { self.source } }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ impl Actor for Back {
|
|||
|
||||
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||
if let Some(u) = cx.tab_mut().backstack.shift_backward().cloned() {
|
||||
return act!(mgr:cd, cx, (u, CdSource::Back));
|
||||
act!(mgr:cd, cx, (u, CdSource::Back))?;
|
||||
}
|
||||
succ!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use yazi_fs::{File, FilesOp, path::expand_url};
|
|||
use yazi_macro::{act, err, render, succ};
|
||||
use yazi_parser::mgr::CdOpt;
|
||||
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
|
||||
use yazi_shared::{Debounce, data::Data, errors::InputError, url::{AsUrl, UrlBuf, UrlLike}};
|
||||
use yazi_shared::{Debounce, data::Data, errors::InputError, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
@ -33,18 +33,8 @@ impl Actor for Cd {
|
|||
}
|
||||
|
||||
// Take parent to history
|
||||
if let Some(rep) = tab.parent.take() {
|
||||
tab.history.insert(rep.url.to_owned(), rep);
|
||||
}
|
||||
|
||||
// Backstack
|
||||
if opt.source.big_jump() {
|
||||
if tab.current.url.is_regular() {
|
||||
tab.backstack.push(tab.current.url.as_url());
|
||||
}
|
||||
if opt.target.is_regular() {
|
||||
tab.backstack.push(opt.target.as_url());
|
||||
}
|
||||
if let Some(t) = tab.parent.take() {
|
||||
tab.history.insert(t.url.to_owned(), t);
|
||||
}
|
||||
|
||||
// Current
|
||||
|
|
@ -58,10 +48,12 @@ impl Actor for Cd {
|
|||
}
|
||||
|
||||
err!(Pubsub::pub_after_cd(tab.id, tab.cwd()));
|
||||
act!(mgr:displace, cx)?;
|
||||
act!(mgr:hidden, cx)?;
|
||||
act!(mgr:sort, cx)?;
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:refresh, cx)?;
|
||||
act!(mgr:stash, cx, opt).ok();
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
yazi-actor/src/mgr/displace.rs
Normal file
59
yazi-actor/src/mgr/displace.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use anyhow::{Result, bail};
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::{VoidOpt, mgr::{CdSource, DisplaceDoOpt}};
|
||||
use yazi_proxy::MgrProxy;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
use yazi_vfs::provider;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Displace;
|
||||
|
||||
impl Actor for Displace {
|
||||
type Options = VoidOpt;
|
||||
|
||||
const NAME: &str = "displace";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||
if cx.cwd().is_absolute() {
|
||||
succ!();
|
||||
}
|
||||
|
||||
let tab = cx.tab().id;
|
||||
let from = cx.cwd().to_owned();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(to) = provider::absolute(&from).await
|
||||
&& to.is_owned()
|
||||
{
|
||||
MgrProxy::displace_do(tab, DisplaceDoOpt { to: to.into(), from });
|
||||
}
|
||||
});
|
||||
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Do
|
||||
pub struct DisplaceDo;
|
||||
|
||||
impl Actor for DisplaceDo {
|
||||
type Options = DisplaceDoOpt;
|
||||
|
||||
const NAME: &str = "displace_do";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
if !opt.to.is_absolute() {
|
||||
bail!("Target URL must be absolute");
|
||||
}
|
||||
|
||||
if cx.cwd() != opt.from {
|
||||
succ!()
|
||||
} else if let Some(hovered) = cx.hovered()
|
||||
&& let Ok(url) = opt.to.try_join(hovered.urn())
|
||||
{
|
||||
act!(mgr:reveal, cx, (url, CdSource::Displace))
|
||||
} else {
|
||||
act!(mgr:cd, cx, (opt.to, CdSource::Displace))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,9 +13,9 @@ impl Actor for Forward {
|
|||
const NAME: &str = "forward";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||
match cx.tab_mut().backstack.shift_forward().cloned() {
|
||||
Some(u) => act!(mgr:cd, cx, (u, CdSource::Forward)),
|
||||
None => succ!(),
|
||||
if let Some(u) = cx.tab_mut().backstack.shift_forward().cloned() {
|
||||
act!(mgr:cd, cx, (u, CdSource::Forward))?;
|
||||
}
|
||||
succ!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ yazi_macro::mod_flat!(
|
|||
close
|
||||
copy
|
||||
create
|
||||
displace
|
||||
download
|
||||
enter
|
||||
escape
|
||||
|
|
@ -36,6 +37,7 @@ yazi_macro::mod_flat!(
|
|||
shell
|
||||
sort
|
||||
spot
|
||||
stash
|
||||
suspend
|
||||
tab_close
|
||||
tab_create
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{borrow::Cow, mem, time::Duration};
|
||||
use std::{borrow::Cow, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::pin;
|
||||
|
|
@ -6,7 +6,7 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
|||
use yazi_config::popup::InputCfg;
|
||||
use yazi_fs::{FilesOp, cha::Cha};
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::{VoidOpt, mgr::{SearchOpt, SearchOptVia}};
|
||||
use yazi_parser::{VoidOpt, mgr::{CdSource, SearchOpt, SearchOptVia}};
|
||||
use yazi_plugin::external;
|
||||
use yazi_proxy::{InputProxy, MgrProxy};
|
||||
use yazi_shared::data::Data;
|
||||
|
|
@ -107,15 +107,10 @@ impl Actor for SearchStop {
|
|||
handle.abort();
|
||||
}
|
||||
|
||||
if !tab.cwd().is_search() {
|
||||
succ!();
|
||||
if tab.cwd().is_search() {
|
||||
act!(mgr:cd, cx, (tab.cwd().to_regular()?, CdSource::Escape))?;
|
||||
}
|
||||
|
||||
let rep = tab.history.remove_or(tab.cwd().to_regular()?);
|
||||
drop(mem::replace(&mut tab.current, rep));
|
||||
|
||||
act!(mgr:hidden, cx)?;
|
||||
act!(mgr:sort, cx)?;
|
||||
act!(mgr:refresh, cx)
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
yazi-actor/src/mgr/stash.rs
Normal file
31
yazi-actor/src/mgr/stash.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use anyhow::Result;
|
||||
use yazi_dds::spark::SparkKind;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::mgr::StashOpt;
|
||||
use yazi_shared::{Source, data::Data, url::AsUrl};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Stash;
|
||||
|
||||
impl Actor for Stash {
|
||||
type Options = StashOpt;
|
||||
|
||||
const NAME: &str = "stash";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
if opt.target.is_regular() {
|
||||
cx.tab_mut().backstack.push(opt.target.as_url());
|
||||
}
|
||||
|
||||
succ!()
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _opt: &Self::Options) -> Option<SparkKind> {
|
||||
match cx.source() {
|
||||
Source::Relay => Some(SparkKind::RelayStash),
|
||||
Source::Ind => Some(SparkKind::IndStash),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,10 @@ impl From<Url> for yazi_shared::url::UrlBufCov {
|
|||
fn from(value: Url) -> Self { Self(value.inner) }
|
||||
}
|
||||
|
||||
impl From<Url> for yazi_shared::url::UrlCow<'_> {
|
||||
fn from(value: Url) -> Self { value.inner.into() }
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Url {
|
||||
type Error = mlua::Error;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum SortBy {
|
||||
#[default]
|
||||
|
|
|
|||
|
|
@ -67,12 +67,10 @@ impl Ember<'static> {
|
|||
| "trash"
|
||||
| "delete"
|
||||
| "mount"
|
||||
) || kind.starts_with("emit-")
|
||||
|| kind.starts_with("emit-ind-")
|
||||
) || kind.starts_with("key-")
|
||||
|| kind.starts_with("ind-")
|
||||
|| kind.starts_with("key-")
|
||||
|| kind.starts_with("emit-")
|
||||
|| kind.starts_with("relay-")
|
||||
|| kind.starts_with("relay-ind-")
|
||||
{
|
||||
bail!("Cannot construct system event");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@ use std::fmt::Display;
|
|||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum SparkKind {
|
||||
IndStash,
|
||||
KeyQuit,
|
||||
RelayStash,
|
||||
}
|
||||
|
||||
impl AsRef<str> for SparkKind {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
Self::IndStash => "ind-stash",
|
||||
Self::KeyQuit => "key-quit",
|
||||
Self::RelayStash => "relay-stash",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ pub enum Spark<'a> {
|
|||
Close(yazi_parser::mgr::CloseOpt),
|
||||
Copy(yazi_parser::mgr::CopyOpt),
|
||||
Create(yazi_parser::mgr::CreateOpt),
|
||||
Displace(yazi_parser::VoidOpt),
|
||||
DisplaceDo(yazi_parser::mgr::DisplaceDoOpt),
|
||||
Download(yazi_parser::mgr::DownloadOpt),
|
||||
Enter(yazi_parser::VoidOpt),
|
||||
Escape(yazi_parser::mgr::EscapeOpt),
|
||||
|
|
@ -53,6 +55,7 @@ pub enum Spark<'a> {
|
|||
Shell(yazi_parser::mgr::ShellOpt),
|
||||
Sort(yazi_parser::mgr::SortOpt),
|
||||
Spot(yazi_parser::mgr::SpotOpt),
|
||||
Stash(yazi_parser::mgr::StashOpt),
|
||||
Suspend(yazi_parser::VoidOpt),
|
||||
TabClose(yazi_parser::mgr::TabCloseOpt),
|
||||
TabCreate(yazi_parser::mgr::TabCreateOpt),
|
||||
|
|
@ -119,7 +122,9 @@ pub enum Spark<'a> {
|
|||
impl<'a> Spark<'a> {
|
||||
pub fn from_lua(lua: &Lua, kind: SparkKind, value: Value) -> mlua::Result<Self> {
|
||||
Ok(match kind {
|
||||
SparkKind::IndStash => Self::Stash(<_>::from_lua(value, lua)?),
|
||||
SparkKind::KeyQuit => Self::Quit(<_>::from_lua(value, lua)?),
|
||||
SparkKind::RelayStash => Self::Stash(<_>::from_lua(value, lua)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -138,6 +143,8 @@ impl<'a> IntoLua for Spark<'a> {
|
|||
Self::Close(b) => b.into_lua(lua),
|
||||
Self::Copy(b) => b.into_lua(lua),
|
||||
Self::Create(b) => b.into_lua(lua),
|
||||
Self::Displace(b) => b.into_lua(lua),
|
||||
Self::DisplaceDo(b) => b.into_lua(lua),
|
||||
Self::Download(b) => b.into_lua(lua),
|
||||
Self::Enter(b) => b.into_lua(lua),
|
||||
Self::Escape(b) => b.into_lua(lua),
|
||||
|
|
@ -176,6 +183,7 @@ impl<'a> IntoLua for Spark<'a> {
|
|||
Self::Shell(b) => b.into_lua(lua),
|
||||
Self::Sort(b) => b.into_lua(lua),
|
||||
Self::Spot(b) => b.into_lua(lua),
|
||||
Self::Stash(b) => b.into_lua(lua),
|
||||
Self::Suspend(b) => b.into_lua(lua),
|
||||
Self::TabClose(b) => b.into_lua(lua),
|
||||
Self::TabCreate(b) => b.into_lua(lua),
|
||||
|
|
@ -282,6 +290,7 @@ try_from_spark!(mgr::CdOpt, mgr:cd);
|
|||
try_from_spark!(mgr::CloseOpt, mgr:close);
|
||||
try_from_spark!(mgr::CopyOpt, mgr:copy);
|
||||
try_from_spark!(mgr::CreateOpt, mgr:create);
|
||||
try_from_spark!(mgr::DisplaceDoOpt, mgr:displace_do);
|
||||
try_from_spark!(mgr::DownloadOpt, mgr:download);
|
||||
try_from_spark!(mgr::EscapeOpt, mgr:escape);
|
||||
try_from_spark!(mgr::FilterOpt, mgr:filter, mgr:filter_do);
|
||||
|
|
@ -306,6 +315,7 @@ try_from_spark!(mgr::SeekOpt, mgr:seek);
|
|||
try_from_spark!(mgr::ShellOpt, mgr:shell);
|
||||
try_from_spark!(mgr::SortOpt, mgr:sort);
|
||||
try_from_spark!(mgr::SpotOpt, mgr:spot);
|
||||
try_from_spark!(mgr::StashOpt, mgr:stash);
|
||||
try_from_spark!(mgr::TabCloseOpt, mgr:tab_close);
|
||||
try_from_spark!(mgr::TabCreateOpt, mgr:tab_create);
|
||||
try_from_spark!(mgr::TabSwitchOpt, mgr:tab_switch);
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ impl<'a> Executor<'a> {
|
|||
// VFS
|
||||
on!(download);
|
||||
on!(upload);
|
||||
on!(displace_do);
|
||||
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> {
|
|||
if url.has_base() { 0 } else { 2 },
|
||||
if url.has_trail() { 0 } else { 2 },
|
||||
)
|
||||
.expect("Failed to create Loc from drive letter")
|
||||
.expect("Loc from drive letter")
|
||||
} else if let Ok(rest) = path.strip_prefix("~/")
|
||||
&& let Some(home) = dirs::home_dir()
|
||||
&& home.is_absolute()
|
||||
|
|
@ -103,7 +103,7 @@ fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> {
|
|||
url.uri().components().count() + if url.has_base() { 0 } else { add },
|
||||
url.urn().components().count() + if url.has_trail() { 0 } else { add },
|
||||
)
|
||||
.expect("Failed to create Loc from home directory")
|
||||
.expect("Loc from home directory")
|
||||
} else if !url.is_absolute() {
|
||||
let cwd = CWD.path();
|
||||
LocBuf::<PathBuf>::with(
|
||||
|
|
@ -111,7 +111,7 @@ fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> {
|
|||
url.uri().components().count(),
|
||||
url.urn().components().count(),
|
||||
)
|
||||
.expect("Failed to create Loc from relative path")
|
||||
.expect("Loc from relative path")
|
||||
} else {
|
||||
return url;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_fs::path::expand_url;
|
||||
use yazi_shared::{event::CmdCow, url::{Url, UrlBuf, UrlCow}};
|
||||
|
||||
|
|
@ -46,7 +47,8 @@ impl IntoLua for CdOpt {
|
|||
}
|
||||
|
||||
// --- Source
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum CdSource {
|
||||
Tab,
|
||||
Cd,
|
||||
|
|
@ -55,9 +57,6 @@ pub enum CdSource {
|
|||
Leave,
|
||||
Forward,
|
||||
Back,
|
||||
}
|
||||
|
||||
impl CdSource {
|
||||
#[inline]
|
||||
pub fn big_jump(self) -> bool { self == Self::Cd || self == Self::Reveal }
|
||||
Escape,
|
||||
Displace,
|
||||
}
|
||||
|
|
|
|||
29
yazi-parser/src/mgr/displace_do.rs
Normal file
29
yazi-parser/src/mgr/displace_do.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DisplaceDoOpt {
|
||||
pub to: UrlBuf,
|
||||
pub from: UrlBuf,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for DisplaceDoOpt {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||
if let Some(opt) = c.take_any2("opt") {
|
||||
opt
|
||||
} else {
|
||||
bail!("'opt' is required for DisplaceDoOpt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for DisplaceDoOpt {
|
||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
||||
impl IntoLua for DisplaceDoOpt {
|
||||
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ yazi_macro::mod_flat!(
|
|||
close
|
||||
copy
|
||||
create
|
||||
displace_do
|
||||
download
|
||||
escape
|
||||
filter
|
||||
|
|
@ -27,6 +28,7 @@ yazi_macro::mod_flat!(
|
|||
shell
|
||||
sort
|
||||
spot
|
||||
stash
|
||||
tab_close
|
||||
tab_create
|
||||
tab_switch
|
||||
|
|
|
|||
44
yazi-parser/src/mgr/stash.rs
Normal file
44
yazi-parser/src/mgr/stash.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_binding::Url;
|
||||
use yazi_shared::{event::CmdCow, url::UrlCow};
|
||||
|
||||
use crate::mgr::{CdOpt, CdSource};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct StashOpt {
|
||||
pub target: UrlCow<'static>,
|
||||
pub source: CdSource,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for StashOpt {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(_: CmdCow) -> Result<Self, Self::Error> { bail!("unsupported") }
|
||||
}
|
||||
|
||||
impl From<CdOpt> for StashOpt {
|
||||
fn from(opt: CdOpt) -> Self { Self { target: opt.target, source: opt.source } }
|
||||
}
|
||||
|
||||
impl FromLua for StashOpt {
|
||||
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
|
||||
let tbl = value.as_table().ok_or_else(|| "expected table".into_lua_err())?;
|
||||
Ok(Self {
|
||||
target: tbl.get::<Url>("target")?.into(),
|
||||
source: lua.from_value(tbl.get("source")?)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for StashOpt {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
lua
|
||||
.create_table_from([
|
||||
("target", Url::new(self.target).into_lua(lua)?),
|
||||
("source", lua.to_value(&self.source)?),
|
||||
])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use mlua::{FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::event::CmdCow;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
|
|
@ -13,9 +13,9 @@ impl From<()> for VoidOpt {
|
|||
}
|
||||
|
||||
impl FromLua for VoidOpt {
|
||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Ok(Self) }
|
||||
}
|
||||
|
||||
impl IntoLua for VoidOpt {
|
||||
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.create_table()?.into_lua(lua) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function M:fetch(job)
|
|||
if file.cache then
|
||||
urls[i], paths[i] = tostring(file.url), tostring(file.cache)
|
||||
else
|
||||
paths[i] = tostring(file.url)
|
||||
paths[i] = tostring(file.url.path)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,30 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use yazi_macro::{emit, relay};
|
||||
use yazi_parser::mgr::{FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt, UpdatePeekedOpt, UpdateSpottedOpt};
|
||||
use yazi_shared::{SStr, url::UrlBuf};
|
||||
use yazi_parser::mgr::{DisplaceDoOpt, FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt, UpdatePeekedOpt, UpdateSpottedOpt};
|
||||
use yazi_shared::{Id, SStr, url::UrlBuf};
|
||||
|
||||
pub struct MgrProxy;
|
||||
|
||||
impl MgrProxy {
|
||||
pub fn arrow(step: impl Into<SStr>) {
|
||||
emit!(Call(relay!(mgr:arrow, [step.into()])));
|
||||
}
|
||||
|
||||
pub fn cd(target: &UrlBuf) {
|
||||
emit!(Call(relay!(mgr:cd, [target]).with("raw", true)));
|
||||
}
|
||||
|
||||
pub fn reveal(target: &UrlBuf) {
|
||||
emit!(Call(relay!(mgr:reveal, [target]).with("raw", true).with("no-dummy", true)));
|
||||
pub fn displace_do(tab: Id, opt: DisplaceDoOpt) {
|
||||
emit!(Call(relay!(mgr:displace_do).with("tab", tab).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn arrow(step: impl Into<SStr>) {
|
||||
emit!(Call(relay!(mgr:arrow, [step.into()])));
|
||||
pub fn filter_do(opt: FilterOpt) {
|
||||
emit!(Call(relay!(mgr:filter_do).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn find_do(opt: FindDoOpt) {
|
||||
emit!(Call(relay!(mgr:find_do).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn open(opt: OpenOpt) {
|
||||
|
|
@ -33,12 +41,8 @@ impl MgrProxy {
|
|||
));
|
||||
}
|
||||
|
||||
pub fn find_do(opt: FindDoOpt) {
|
||||
emit!(Call(relay!(mgr:find_do).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn filter_do(opt: FilterOpt) {
|
||||
emit!(Call(relay!(mgr:filter_do).with_any("opt", opt)));
|
||||
pub fn reveal(target: &UrlBuf) {
|
||||
emit!(Call(relay!(mgr:reveal, [target]).with("raw", true).with("no-dummy", true)));
|
||||
}
|
||||
|
||||
pub fn search_do(opt: SearchOpt) {
|
||||
|
|
@ -50,8 +54,8 @@ impl MgrProxy {
|
|||
));
|
||||
}
|
||||
|
||||
pub fn watch() {
|
||||
emit!(Call(relay!(mgr:watch)));
|
||||
pub fn update_paged_by(page: usize, only_if: &UrlBuf) {
|
||||
emit!(Call(relay!(mgr:update_paged, [page]).with("only-if", only_if)));
|
||||
}
|
||||
|
||||
pub fn update_peeked(opt: UpdatePeekedOpt) {
|
||||
|
|
@ -62,14 +66,14 @@ impl MgrProxy {
|
|||
emit!(Call(relay!(mgr:update_spotted).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn update_paged_by(page: usize, only_if: &UrlBuf) {
|
||||
emit!(Call(relay!(mgr:update_paged, [page]).with("only-if", only_if)));
|
||||
}
|
||||
|
||||
pub fn upload<I>(urls: I)
|
||||
where
|
||||
I: IntoIterator<Item = UrlBuf>,
|
||||
{
|
||||
emit!(Call(relay!(mgr:upload).with_seq(urls)));
|
||||
}
|
||||
|
||||
pub fn watch() {
|
||||
emit!(Call(relay!(mgr:watch)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,40 +28,6 @@ pub enum Data {
|
|||
Any(Box<dyn Any + Send + Sync>),
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_string(self) -> Option<SStr> {
|
||||
match self {
|
||||
Self::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_any<T: 'static>(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Any(b) => b.downcast::<T>().ok().map(|b| *b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: find a better name
|
||||
pub fn into_any2<T: 'static>(self) -> Result<T> {
|
||||
if let Self::Any(b) = self
|
||||
&& let Ok(t) = b.downcast::<T>()
|
||||
{
|
||||
Ok(*t)
|
||||
} else {
|
||||
bail!("Failed to downcast Data into {}", std::any::type_name::<T>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for Data {
|
||||
fn from(_: ()) -> Self { Self::Nil }
|
||||
}
|
||||
|
|
@ -201,13 +167,291 @@ impl<'a> TryFrom<&'a Data> for UrlCow<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a Data> for &'a [u8] {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Data::Bytes(b) => Ok(b),
|
||||
_ => bail!("not bytes"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<bool> for Data {
|
||||
fn eq(&self, other: &bool) -> bool { self.try_into().is_ok_and(|b| *other == b) }
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_string(self) -> Option<SStr> {
|
||||
match self {
|
||||
Self::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_any<T: 'static>(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Any(b) => b.downcast::<T>().ok().map(|b| *b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: find a better name
|
||||
pub fn into_any2<T: 'static>(self) -> Result<T> {
|
||||
if let Self::Any(b) = self
|
||||
&& let Ok(t) = b.downcast::<T>()
|
||||
{
|
||||
Ok(*t)
|
||||
} else {
|
||||
bail!("Failed to downcast Data into {}", std::any::type_name::<T>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserializer<'de> for &Data {
|
||||
type Error = serde::de::value::Error;
|
||||
|
||||
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("any not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_bool(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i8(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i16(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i32(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i64(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u8(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u16(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u32(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u64(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f32(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f64(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("not a char"))
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_str(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("string not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_bytes(self.try_into().map_err(serde::de::Error::custom)?)
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("byte buf not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::Nil => visitor.visit_none(),
|
||||
_ => visitor.visit_some(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_unit()
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_unit()
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("seq not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("tuple not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
_visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("tuple struct not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("map not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
_visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("struct not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
_visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("enum not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_identifier<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("identifier not supported"))
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: serde::de::Visitor<'de>,
|
||||
{
|
||||
Err(serde::de::Error::custom("ignored any not supported"))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Macros
|
||||
macro_rules! impl_into_integer {
|
||||
($t:ty, $name:ident) => {
|
||||
($t:ty) => {
|
||||
impl TryFrom<&Data> for $t {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
|
|
@ -224,14 +468,14 @@ macro_rules! impl_into_integer {
|
|||
}
|
||||
|
||||
macro_rules! impl_into_number {
|
||||
($t:ty, $name:ident) => {
|
||||
($t:ty) => {
|
||||
impl TryFrom<&Data> for $t {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &Data) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
Data::Integer(i) if *i == (*i as $t as _) => *i as $t,
|
||||
Data::Number(n) => <$t>::try_from(*n)?,
|
||||
Data::Number(n) if *n == (*n as $t as _) => *n as $t,
|
||||
Data::String(s) => s.parse()?,
|
||||
Data::Id(i) if i.0 == (i.0 as $t as _) => i.0 as $t,
|
||||
_ => bail!("not a number"),
|
||||
|
|
@ -241,10 +485,17 @@ macro_rules! impl_into_number {
|
|||
};
|
||||
}
|
||||
|
||||
impl_into_integer!(usize, as_usize);
|
||||
impl_into_integer!(isize, as_isize);
|
||||
impl_into_integer!(i16, as_i16);
|
||||
impl_into_integer!(i32, as_i32);
|
||||
impl_into_integer!(crate::Id, as_id);
|
||||
impl_into_integer!(i8);
|
||||
impl_into_integer!(i16);
|
||||
impl_into_integer!(i32);
|
||||
impl_into_integer!(i64);
|
||||
impl_into_integer!(isize);
|
||||
impl_into_integer!(u8);
|
||||
impl_into_integer!(u16);
|
||||
impl_into_integer!(u32);
|
||||
impl_into_integer!(u64);
|
||||
impl_into_integer!(usize);
|
||||
impl_into_integer!(crate::Id);
|
||||
|
||||
impl_into_number!(f64, as_f64);
|
||||
impl_into_number!(f32);
|
||||
impl_into_number!(f64);
|
||||
|
|
|
|||
|
|
@ -152,6 +152,14 @@ impl Cmd {
|
|||
self.take(0)
|
||||
}
|
||||
|
||||
pub fn take_second<T>(&mut self) -> Result<T>
|
||||
where
|
||||
T: TryFrom<Data>,
|
||||
T::Error: Into<anyhow::Error>,
|
||||
{
|
||||
self.take(1)
|
||||
}
|
||||
|
||||
pub fn take_seq<T>(&mut self) -> Vec<T>
|
||||
where
|
||||
T: TryFrom<Data>,
|
||||
|
|
|
|||
|
|
@ -55,6 +55,18 @@ impl CmdCow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn take_second<'a, T>(&mut self) -> Result<T>
|
||||
where
|
||||
T: TryFrom<Data> + TryFrom<&'a Data>,
|
||||
<T as TryFrom<Data>>::Error: Into<anyhow::Error>,
|
||||
<T as TryFrom<&'a Data>>::Error: Into<anyhow::Error>,
|
||||
{
|
||||
match self {
|
||||
Self::Owned(c) => c.take_second(),
|
||||
Self::Borrowed(c) => c.second(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_seq<'a, T>(&mut self) -> Vec<T>
|
||||
where
|
||||
T: TryFrom<Data> + TryFrom<&'a Data>,
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ pub(super) trait LocAbleImpl<'p>: LocAble<'p> {
|
|||
|
||||
fn len(self) -> usize { self.as_encoded_bytes().len() }
|
||||
|
||||
fn parent(self) -> Option<Self>;
|
||||
|
||||
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
||||
where
|
||||
T: AsStrandView<'a, Self::Strand<'a>>;
|
||||
|
|
@ -99,6 +101,8 @@ impl<'p> LocAbleImpl<'p> for &'p std::path::Path {
|
|||
self.join(path.as_strand_view())
|
||||
}
|
||||
|
||||
fn parent(self) -> Option<Self> { self.parent() }
|
||||
|
||||
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
||||
where
|
||||
T: AsStrandView<'a, Self::Strand<'a>>,
|
||||
|
|
@ -129,6 +133,8 @@ impl<'p> LocAbleImpl<'p> for &'p typed_path::UnixPath {
|
|||
self.join(path.as_strand_view())
|
||||
}
|
||||
|
||||
fn parent(self) -> Option<Self> { self.parent() }
|
||||
|
||||
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
||||
where
|
||||
T: AsStrandView<'a, Self::Strand<'a>>,
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ where
|
|||
let old = self.inner.len();
|
||||
self.mutate(|path| path.set_file_name(name));
|
||||
|
||||
let new = self.len();
|
||||
let new = self.inner.len();
|
||||
if new == old {
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -215,6 +215,9 @@ where
|
|||
loc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn parent(&self) -> Option<P::Borrowed<'_>> { self.as_loc().parent() }
|
||||
|
||||
#[inline]
|
||||
fn mutate<T, F: FnOnce(&mut P) -> T>(&mut self, f: F) -> T {
|
||||
let mut inner = mem::take(&mut self.inner);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,64 @@ impl<'p, P> Loc<'p, P>
|
|||
where
|
||||
P: LocAble<'p> + LocAbleImpl<'p>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn as_inner(self) -> P { self.inner }
|
||||
|
||||
#[inline]
|
||||
pub fn as_loc(self) -> Self { self }
|
||||
|
||||
pub fn bare<T>(path: T) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
{
|
||||
let path = path.as_path_view();
|
||||
let Some(name) = path.file_name() else {
|
||||
let uri = path.strip_prefix(P::empty()).unwrap().len();
|
||||
return Self { inner: path, uri, urn: 0, _phantom: PhantomData };
|
||||
};
|
||||
|
||||
let name_len = name.len();
|
||||
let prefix_len = unsafe {
|
||||
name.as_encoded_bytes().as_ptr().offset_from_unsigned(path.as_encoded_bytes().as_ptr())
|
||||
};
|
||||
|
||||
let bytes = &path.as_encoded_bytes()[..prefix_len + name_len];
|
||||
Self {
|
||||
inner: unsafe { P::from_encoded_bytes_unchecked(bytes) },
|
||||
uri: bytes.len(),
|
||||
urn: name_len,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn base(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.uri),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floated<'a, T, S>(path: T, base: S) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
S: AsStrandView<'a, P::Strand<'a>>,
|
||||
{
|
||||
let mut loc = Self::bare(path);
|
||||
loc.uri = loc.inner.strip_prefix(base).expect("Loc must start with the given base").len();
|
||||
loc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_base(self) -> bool { self.inner.len() != self.uri }
|
||||
|
||||
#[inline]
|
||||
pub fn has_trail(self) -> bool { self.inner.len() != self.urn }
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(self) -> bool { self.inner.len() == 0 }
|
||||
|
||||
pub fn new<'a, T, S>(path: T, base: S, trail: S) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
|
|
@ -84,6 +142,67 @@ where
|
|||
loc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn parent(self) -> Option<P> {
|
||||
self.inner.parent().filter(|p| !p.as_encoded_bytes().is_empty())
|
||||
}
|
||||
|
||||
pub fn saturated<'a, T>(path: T, kind: SchemeKind) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
{
|
||||
match kind {
|
||||
SchemeKind::Regular => Self::bare(path),
|
||||
SchemeKind::Search => Self::zeroed(path),
|
||||
SchemeKind::Archive => Self::zeroed(path),
|
||||
SchemeKind::Sftp => Self::bare(path),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn trail(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.urn),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn triple(self) -> (P, P, P) {
|
||||
let len = self.inner.len();
|
||||
|
||||
let base = ..len - self.uri;
|
||||
let rest = len - self.uri..len - self.urn;
|
||||
let urn = len - self.urn..;
|
||||
|
||||
unsafe {
|
||||
(
|
||||
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(base)),
|
||||
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(rest)),
|
||||
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(urn)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn uri(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.uri..),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn urn(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.urn..),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with<T>(path: T, uri: usize, urn: usize) -> Result<Self>
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
|
|
@ -116,30 +235,6 @@ where
|
|||
Ok(loc)
|
||||
}
|
||||
|
||||
pub fn bare<T>(path: T) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
{
|
||||
let path = path.as_path_view();
|
||||
let Some(name) = path.file_name() else {
|
||||
let uri = path.strip_prefix(P::empty()).unwrap().len();
|
||||
return Self { inner: path, uri, urn: 0, _phantom: PhantomData };
|
||||
};
|
||||
|
||||
let name_len = name.len();
|
||||
let prefix_len = unsafe {
|
||||
name.as_encoded_bytes().as_ptr().offset_from_unsigned(path.as_encoded_bytes().as_ptr())
|
||||
};
|
||||
|
||||
let bytes = &path.as_encoded_bytes()[..prefix_len + name_len];
|
||||
Self {
|
||||
inner: unsafe { P::from_encoded_bytes_unchecked(bytes) },
|
||||
uri: bytes.len(),
|
||||
urn: name_len,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zeroed<T>(path: T) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
|
|
@ -148,94 +243,4 @@ where
|
|||
(loc.uri, loc.urn) = (0, 0);
|
||||
loc
|
||||
}
|
||||
|
||||
pub fn floated<'a, T, S>(path: T, base: S) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
S: AsStrandView<'a, P::Strand<'a>>,
|
||||
{
|
||||
let mut loc = Self::bare(path);
|
||||
loc.uri = loc.inner.strip_prefix(base).expect("Loc must start with the given base").len();
|
||||
loc
|
||||
}
|
||||
|
||||
pub fn saturated<'a, T>(path: T, kind: SchemeKind) -> Self
|
||||
where
|
||||
T: AsPathView<'p, P>,
|
||||
{
|
||||
match kind {
|
||||
SchemeKind::Regular => Self::bare(path),
|
||||
SchemeKind::Search => Self::zeroed(path),
|
||||
SchemeKind::Archive => Self::zeroed(path),
|
||||
SchemeKind::Sftp => Self::bare(path),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_loc(self) -> Self { self }
|
||||
|
||||
#[inline]
|
||||
pub fn as_inner(self) -> P { self.inner }
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(self) -> bool { self.inner.len() == 0 }
|
||||
|
||||
#[inline]
|
||||
pub fn uri(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.uri..),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn urn(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(self.inner.len() - self.urn..),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn base(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.uri),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_base(self) -> bool { self.inner.len() != self.uri }
|
||||
|
||||
#[inline]
|
||||
pub fn trail(self) -> P {
|
||||
unsafe {
|
||||
P::from_encoded_bytes_unchecked(
|
||||
self.inner.as_encoded_bytes().get_unchecked(..self.inner.len() - self.urn),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_trail(self) -> bool { self.inner.len() != self.urn }
|
||||
|
||||
#[inline]
|
||||
pub fn triple(self) -> (P, P, P) {
|
||||
let len = self.inner.len();
|
||||
|
||||
let base = ..len - self.uri;
|
||||
let rest = len - self.uri..len - self.urn;
|
||||
let urn = len - self.urn..;
|
||||
|
||||
unsafe {
|
||||
(
|
||||
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(base)),
|
||||
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(rest)),
|
||||
P::from_encoded_bytes_unchecked(self.inner.as_encoded_bytes().get_unchecked(urn)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use anyhow::Result;
|
|||
use crate::path::{AsPath, PathBufDyn, PathDyn, PathKind};
|
||||
|
||||
// --- PathCow
|
||||
#[derive(Debug)]
|
||||
pub enum PathCow<'a> {
|
||||
Borrowed(PathDyn<'a>),
|
||||
Owned(PathBufDyn),
|
||||
|
|
|
|||
|
|
@ -139,8 +139,8 @@ impl<'p> PathDyn<'p> {
|
|||
|
||||
pub fn parent(self) -> Option<Self> {
|
||||
Some(match self {
|
||||
Self::Os(p) => Self::Os(p.parent()?),
|
||||
Self::Unix(p) => Self::Unix(p.parent()?),
|
||||
Self::Os(p) => Self::Os(p.parent().filter(|p| !p.as_os_str().is_empty())?),
|
||||
Self::Unix(p) => Self::Unix(p.parent().filter(|p| !p.as_bytes().is_empty())?),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ pub enum Source {
|
|||
Unknown,
|
||||
|
||||
Key,
|
||||
Ind,
|
||||
|
||||
Emit,
|
||||
EmitInd,
|
||||
|
||||
Relay,
|
||||
RelayInd,
|
||||
|
||||
Ind,
|
||||
}
|
||||
|
||||
impl Source {
|
||||
#[inline]
|
||||
pub fn is_key(self) -> bool { self == Self::Key }
|
||||
|
||||
#[inline]
|
||||
pub fn is_ind(self) -> bool { self == Self::Ind }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ impl PartialEq for UrlBuf {
|
|||
fn eq(&self, other: &Self) -> bool { self.as_url() == other.as_url() }
|
||||
}
|
||||
|
||||
impl PartialEq<UrlBuf> for &UrlBuf {
|
||||
fn eq(&self, other: &UrlBuf) -> bool { self.as_url() == other.as_url() }
|
||||
}
|
||||
|
||||
impl PartialEq<Url<'_>> for UrlBuf {
|
||||
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
||||
}
|
||||
|
|
@ -104,6 +108,14 @@ impl PartialEq<Url<'_>> for &UrlBuf {
|
|||
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
||||
}
|
||||
|
||||
impl PartialEq<UrlCow<'_>> for UrlBuf {
|
||||
fn eq(&self, other: &UrlCow) -> bool { self.as_url() == other.as_url() }
|
||||
}
|
||||
|
||||
impl PartialEq<UrlCow<'_>> for &UrlBuf {
|
||||
fn eq(&self, other: &UrlCow) -> bool { self.as_url() == other.as_url() }
|
||||
}
|
||||
|
||||
// --- Hash
|
||||
impl Hash for UrlBuf {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.as_url().hash(state) }
|
||||
|
|
@ -293,7 +305,7 @@ mod tests {
|
|||
("sftp://remote//", None),
|
||||
// Relative
|
||||
("search://kw:2:2/a/b", Some("search://kw:1:1/a")),
|
||||
("search://kw:1:1/a", Some("search://kw/")),
|
||||
("search://kw:1:1/a", None),
|
||||
("search://kw/", None),
|
||||
];
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue