mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +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 }
|
pub fn tasks(&self) -> &Tasks { &self.tasks }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn source(&self) -> Source {
|
pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ impl Actor for Back {
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||||
if let Some(u) = cx.tab_mut().backstack.shift_backward().cloned() {
|
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!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use yazi_fs::{File, FilesOp, path::expand_url};
|
||||||
use yazi_macro::{act, err, render, succ};
|
use yazi_macro::{act, err, render, succ};
|
||||||
use yazi_parser::mgr::CdOpt;
|
use yazi_parser::mgr::CdOpt;
|
||||||
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
|
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 yazi_vfs::VfsFile;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -33,18 +33,8 @@ impl Actor for Cd {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take parent to history
|
// Take parent to history
|
||||||
if let Some(rep) = tab.parent.take() {
|
if let Some(t) = tab.parent.take() {
|
||||||
tab.history.insert(rep.url.to_owned(), rep);
|
tab.history.insert(t.url.to_owned(), t);
|
||||||
}
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current
|
// Current
|
||||||
|
|
@ -58,10 +48,12 @@ impl Actor for Cd {
|
||||||
}
|
}
|
||||||
|
|
||||||
err!(Pubsub::pub_after_cd(tab.id, tab.cwd()));
|
err!(Pubsub::pub_after_cd(tab.id, tab.cwd()));
|
||||||
|
act!(mgr:displace, cx)?;
|
||||||
act!(mgr:hidden, cx)?;
|
act!(mgr:hidden, cx)?;
|
||||||
act!(mgr:sort, cx)?;
|
act!(mgr:sort, cx)?;
|
||||||
act!(mgr:hover, cx)?;
|
act!(mgr:hover, cx)?;
|
||||||
act!(mgr:refresh, cx)?;
|
act!(mgr:refresh, cx)?;
|
||||||
|
act!(mgr:stash, cx, opt).ok();
|
||||||
succ!(render!());
|
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";
|
const NAME: &str = "forward";
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||||
match cx.tab_mut().backstack.shift_forward().cloned() {
|
if let Some(u) = cx.tab_mut().backstack.shift_forward().cloned() {
|
||||||
Some(u) => act!(mgr:cd, cx, (u, CdSource::Forward)),
|
act!(mgr:cd, cx, (u, CdSource::Forward))?;
|
||||||
None => succ!(),
|
|
||||||
}
|
}
|
||||||
|
succ!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ yazi_macro::mod_flat!(
|
||||||
close
|
close
|
||||||
copy
|
copy
|
||||||
create
|
create
|
||||||
|
displace
|
||||||
download
|
download
|
||||||
enter
|
enter
|
||||||
escape
|
escape
|
||||||
|
|
@ -36,6 +37,7 @@ yazi_macro::mod_flat!(
|
||||||
shell
|
shell
|
||||||
sort
|
sort
|
||||||
spot
|
spot
|
||||||
|
stash
|
||||||
suspend
|
suspend
|
||||||
tab_close
|
tab_close
|
||||||
tab_create
|
tab_create
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::Cow, mem, time::Duration};
|
use std::{borrow::Cow, time::Duration};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::pin;
|
use tokio::pin;
|
||||||
|
|
@ -6,7 +6,7 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_fs::{FilesOp, cha::Cha};
|
use yazi_fs::{FilesOp, cha::Cha};
|
||||||
use yazi_macro::{act, succ};
|
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_plugin::external;
|
||||||
use yazi_proxy::{InputProxy, MgrProxy};
|
use yazi_proxy::{InputProxy, MgrProxy};
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
@ -107,15 +107,10 @@ impl Actor for SearchStop {
|
||||||
handle.abort();
|
handle.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tab.cwd().is_search() {
|
if tab.cwd().is_search() {
|
||||||
succ!();
|
act!(mgr:cd, cx, (tab.cwd().to_regular()?, CdSource::Escape))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rep = tab.history.remove_or(tab.cwd().to_regular()?);
|
succ!();
|
||||||
drop(mem::replace(&mut tab.current, rep));
|
|
||||||
|
|
||||||
act!(mgr:hidden, cx)?;
|
|
||||||
act!(mgr:sort, cx)?;
|
|
||||||
act!(mgr:refresh, cx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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) }
|
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 {
|
impl TryFrom<&[u8]> for Url {
|
||||||
type Error = mlua::Error;
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
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")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum SortBy {
|
pub enum SortBy {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
||||||
|
|
@ -67,12 +67,10 @@ impl Ember<'static> {
|
||||||
| "trash"
|
| "trash"
|
||||||
| "delete"
|
| "delete"
|
||||||
| "mount"
|
| "mount"
|
||||||
) || kind.starts_with("emit-")
|
) || kind.starts_with("key-")
|
||||||
|| kind.starts_with("emit-ind-")
|
|
||||||
|| kind.starts_with("ind-")
|
|| kind.starts_with("ind-")
|
||||||
|| kind.starts_with("key-")
|
|| kind.starts_with("emit-")
|
||||||
|| kind.starts_with("relay-")
|
|| kind.starts_with("relay-")
|
||||||
|| kind.starts_with("relay-ind-")
|
|
||||||
{
|
{
|
||||||
bail!("Cannot construct system event");
|
bail!("Cannot construct system event");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,17 @@ use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum SparkKind {
|
pub enum SparkKind {
|
||||||
|
IndStash,
|
||||||
KeyQuit,
|
KeyQuit,
|
||||||
|
RelayStash,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<str> for SparkKind {
|
impl AsRef<str> for SparkKind {
|
||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
|
Self::IndStash => "ind-stash",
|
||||||
Self::KeyQuit => "key-quit",
|
Self::KeyQuit => "key-quit",
|
||||||
|
Self::RelayStash => "relay-stash",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ pub enum Spark<'a> {
|
||||||
Close(yazi_parser::mgr::CloseOpt),
|
Close(yazi_parser::mgr::CloseOpt),
|
||||||
Copy(yazi_parser::mgr::CopyOpt),
|
Copy(yazi_parser::mgr::CopyOpt),
|
||||||
Create(yazi_parser::mgr::CreateOpt),
|
Create(yazi_parser::mgr::CreateOpt),
|
||||||
|
Displace(yazi_parser::VoidOpt),
|
||||||
|
DisplaceDo(yazi_parser::mgr::DisplaceDoOpt),
|
||||||
Download(yazi_parser::mgr::DownloadOpt),
|
Download(yazi_parser::mgr::DownloadOpt),
|
||||||
Enter(yazi_parser::VoidOpt),
|
Enter(yazi_parser::VoidOpt),
|
||||||
Escape(yazi_parser::mgr::EscapeOpt),
|
Escape(yazi_parser::mgr::EscapeOpt),
|
||||||
|
|
@ -53,6 +55,7 @@ pub enum Spark<'a> {
|
||||||
Shell(yazi_parser::mgr::ShellOpt),
|
Shell(yazi_parser::mgr::ShellOpt),
|
||||||
Sort(yazi_parser::mgr::SortOpt),
|
Sort(yazi_parser::mgr::SortOpt),
|
||||||
Spot(yazi_parser::mgr::SpotOpt),
|
Spot(yazi_parser::mgr::SpotOpt),
|
||||||
|
Stash(yazi_parser::mgr::StashOpt),
|
||||||
Suspend(yazi_parser::VoidOpt),
|
Suspend(yazi_parser::VoidOpt),
|
||||||
TabClose(yazi_parser::mgr::TabCloseOpt),
|
TabClose(yazi_parser::mgr::TabCloseOpt),
|
||||||
TabCreate(yazi_parser::mgr::TabCreateOpt),
|
TabCreate(yazi_parser::mgr::TabCreateOpt),
|
||||||
|
|
@ -119,7 +122,9 @@ pub enum Spark<'a> {
|
||||||
impl<'a> Spark<'a> {
|
impl<'a> Spark<'a> {
|
||||||
pub fn from_lua(lua: &Lua, kind: SparkKind, value: Value) -> mlua::Result<Self> {
|
pub fn from_lua(lua: &Lua, kind: SparkKind, value: Value) -> mlua::Result<Self> {
|
||||||
Ok(match kind {
|
Ok(match kind {
|
||||||
|
SparkKind::IndStash => Self::Stash(<_>::from_lua(value, lua)?),
|
||||||
SparkKind::KeyQuit => Self::Quit(<_>::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::Close(b) => b.into_lua(lua),
|
||||||
Self::Copy(b) => b.into_lua(lua),
|
Self::Copy(b) => b.into_lua(lua),
|
||||||
Self::Create(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::Download(b) => b.into_lua(lua),
|
||||||
Self::Enter(b) => b.into_lua(lua),
|
Self::Enter(b) => b.into_lua(lua),
|
||||||
Self::Escape(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::Shell(b) => b.into_lua(lua),
|
||||||
Self::Sort(b) => b.into_lua(lua),
|
Self::Sort(b) => b.into_lua(lua),
|
||||||
Self::Spot(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::Suspend(b) => b.into_lua(lua),
|
||||||
Self::TabClose(b) => b.into_lua(lua),
|
Self::TabClose(b) => b.into_lua(lua),
|
||||||
Self::TabCreate(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::CloseOpt, mgr:close);
|
||||||
try_from_spark!(mgr::CopyOpt, mgr:copy);
|
try_from_spark!(mgr::CopyOpt, mgr:copy);
|
||||||
try_from_spark!(mgr::CreateOpt, mgr:create);
|
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::DownloadOpt, mgr:download);
|
||||||
try_from_spark!(mgr::EscapeOpt, mgr:escape);
|
try_from_spark!(mgr::EscapeOpt, mgr:escape);
|
||||||
try_from_spark!(mgr::FilterOpt, mgr:filter, mgr:filter_do);
|
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::ShellOpt, mgr:shell);
|
||||||
try_from_spark!(mgr::SortOpt, mgr:sort);
|
try_from_spark!(mgr::SortOpt, mgr:sort);
|
||||||
try_from_spark!(mgr::SpotOpt, mgr:spot);
|
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::TabCloseOpt, mgr:tab_close);
|
||||||
try_from_spark!(mgr::TabCreateOpt, mgr:tab_create);
|
try_from_spark!(mgr::TabCreateOpt, mgr:tab_create);
|
||||||
try_from_spark!(mgr::TabSwitchOpt, mgr:tab_switch);
|
try_from_spark!(mgr::TabSwitchOpt, mgr:tab_switch);
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ impl<'a> Executor<'a> {
|
||||||
// VFS
|
// VFS
|
||||||
on!(download);
|
on!(download);
|
||||||
on!(upload);
|
on!(upload);
|
||||||
|
on!(displace_do);
|
||||||
|
|
||||||
match cmd.name.as_ref() {
|
match cmd.name.as_ref() {
|
||||||
// Help
|
// 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_base() { 0 } else { 2 },
|
||||||
if url.has_trail() { 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("~/")
|
} else if let Ok(rest) = path.strip_prefix("~/")
|
||||||
&& let Some(home) = dirs::home_dir()
|
&& let Some(home) = dirs::home_dir()
|
||||||
&& home.is_absolute()
|
&& 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.uri().components().count() + if url.has_base() { 0 } else { add },
|
||||||
url.urn().components().count() + if url.has_trail() { 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() {
|
} else if !url.is_absolute() {
|
||||||
let cwd = CWD.path();
|
let cwd = CWD.path();
|
||||||
LocBuf::<PathBuf>::with(
|
LocBuf::<PathBuf>::with(
|
||||||
|
|
@ -111,7 +111,7 @@ fn absolute_url_impl<'a>(url: UrlCow<'a>) -> UrlCow<'a> {
|
||||||
url.uri().components().count(),
|
url.uri().components().count(),
|
||||||
url.urn().components().count(),
|
url.urn().components().count(),
|
||||||
)
|
)
|
||||||
.expect("Failed to create Loc from relative path")
|
.expect("Loc from relative path")
|
||||||
} else {
|
} else {
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use yazi_fs::path::expand_url;
|
use yazi_fs::path::expand_url;
|
||||||
use yazi_shared::{event::CmdCow, url::{Url, UrlBuf, UrlCow}};
|
use yazi_shared::{event::CmdCow, url::{Url, UrlBuf, UrlCow}};
|
||||||
|
|
||||||
|
|
@ -46,7 +47,8 @@ impl IntoLua for CdOpt {
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Source
|
// --- Source
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum CdSource {
|
pub enum CdSource {
|
||||||
Tab,
|
Tab,
|
||||||
Cd,
|
Cd,
|
||||||
|
|
@ -55,9 +57,6 @@ pub enum CdSource {
|
||||||
Leave,
|
Leave,
|
||||||
Forward,
|
Forward,
|
||||||
Back,
|
Back,
|
||||||
}
|
Escape,
|
||||||
|
Displace,
|
||||||
impl CdSource {
|
|
||||||
#[inline]
|
|
||||||
pub fn big_jump(self) -> bool { self == Self::Cd || self == Self::Reveal }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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
|
close
|
||||||
copy
|
copy
|
||||||
create
|
create
|
||||||
|
displace_do
|
||||||
download
|
download
|
||||||
escape
|
escape
|
||||||
filter
|
filter
|
||||||
|
|
@ -27,6 +28,7 @@ yazi_macro::mod_flat!(
|
||||||
shell
|
shell
|
||||||
sort
|
sort
|
||||||
spot
|
spot
|
||||||
|
stash
|
||||||
tab_close
|
tab_close
|
||||||
tab_create
|
tab_create
|
||||||
tab_switch
|
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;
|
use yazi_shared::event::CmdCow;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
|
|
@ -13,9 +13,9 @@ impl From<()> for VoidOpt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromLua 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 {
|
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
|
if file.cache then
|
||||||
urls[i], paths[i] = tostring(file.url), tostring(file.cache)
|
urls[i], paths[i] = tostring(file.url), tostring(file.cache)
|
||||||
else
|
else
|
||||||
paths[i] = tostring(file.url)
|
paths[i] = tostring(file.url.path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,30 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use yazi_macro::{emit, relay};
|
use yazi_macro::{emit, relay};
|
||||||
use yazi_parser::mgr::{FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt, UpdatePeekedOpt, UpdateSpottedOpt};
|
use yazi_parser::mgr::{DisplaceDoOpt, FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt, UpdatePeekedOpt, UpdateSpottedOpt};
|
||||||
use yazi_shared::{SStr, url::UrlBuf};
|
use yazi_shared::{Id, SStr, url::UrlBuf};
|
||||||
|
|
||||||
pub struct MgrProxy;
|
pub struct MgrProxy;
|
||||||
|
|
||||||
impl MgrProxy {
|
impl MgrProxy {
|
||||||
|
pub fn arrow(step: impl Into<SStr>) {
|
||||||
|
emit!(Call(relay!(mgr:arrow, [step.into()])));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cd(target: &UrlBuf) {
|
pub fn cd(target: &UrlBuf) {
|
||||||
emit!(Call(relay!(mgr:cd, [target]).with("raw", true)));
|
emit!(Call(relay!(mgr:cd, [target]).with("raw", true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reveal(target: &UrlBuf) {
|
pub fn displace_do(tab: Id, opt: DisplaceDoOpt) {
|
||||||
emit!(Call(relay!(mgr:reveal, [target]).with("raw", true).with("no-dummy", true)));
|
emit!(Call(relay!(mgr:displace_do).with("tab", tab).with_any("opt", opt)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arrow(step: impl Into<SStr>) {
|
pub fn filter_do(opt: FilterOpt) {
|
||||||
emit!(Call(relay!(mgr:arrow, [step.into()])));
|
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) {
|
pub fn open(opt: OpenOpt) {
|
||||||
|
|
@ -33,12 +41,8 @@ impl MgrProxy {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_do(opt: FindDoOpt) {
|
pub fn reveal(target: &UrlBuf) {
|
||||||
emit!(Call(relay!(mgr:find_do).with_any("opt", opt)));
|
emit!(Call(relay!(mgr:reveal, [target]).with("raw", true).with("no-dummy", true)));
|
||||||
}
|
|
||||||
|
|
||||||
pub fn filter_do(opt: FilterOpt) {
|
|
||||||
emit!(Call(relay!(mgr:filter_do).with_any("opt", opt)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_do(opt: SearchOpt) {
|
pub fn search_do(opt: SearchOpt) {
|
||||||
|
|
@ -50,8 +54,8 @@ impl MgrProxy {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn watch() {
|
pub fn update_paged_by(page: usize, only_if: &UrlBuf) {
|
||||||
emit!(Call(relay!(mgr:watch)));
|
emit!(Call(relay!(mgr:update_paged, [page]).with("only-if", only_if)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_peeked(opt: UpdatePeekedOpt) {
|
pub fn update_peeked(opt: UpdatePeekedOpt) {
|
||||||
|
|
@ -62,14 +66,14 @@ impl MgrProxy {
|
||||||
emit!(Call(relay!(mgr:update_spotted).with_any("opt", opt)));
|
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)
|
pub fn upload<I>(urls: I)
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = UrlBuf>,
|
I: IntoIterator<Item = UrlBuf>,
|
||||||
{
|
{
|
||||||
emit!(Call(relay!(mgr:upload).with_seq(urls)));
|
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>),
|
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 {
|
impl From<()> for Data {
|
||||||
fn from(_: ()) -> Self { Self::Nil }
|
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 {
|
impl PartialEq<bool> for Data {
|
||||||
fn eq(&self, other: &bool) -> bool { self.try_into().is_ok_and(|b| *other == b) }
|
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
|
// --- Macros
|
||||||
macro_rules! impl_into_integer {
|
macro_rules! impl_into_integer {
|
||||||
($t:ty, $name:ident) => {
|
($t:ty) => {
|
||||||
impl TryFrom<&Data> for $t {
|
impl TryFrom<&Data> for $t {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
|
@ -224,14 +468,14 @@ macro_rules! impl_into_integer {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_into_number {
|
macro_rules! impl_into_number {
|
||||||
($t:ty, $name:ident) => {
|
($t:ty) => {
|
||||||
impl TryFrom<&Data> for $t {
|
impl TryFrom<&Data> for $t {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(value: &Data) -> Result<Self, Self::Error> {
|
fn try_from(value: &Data) -> Result<Self, Self::Error> {
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
Data::Integer(i) if *i == (*i as $t as _) => *i as $t,
|
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::String(s) => s.parse()?,
|
||||||
Data::Id(i) if i.0 == (i.0 as $t as _) => i.0 as $t,
|
Data::Id(i) if i.0 == (i.0 as $t as _) => i.0 as $t,
|
||||||
_ => bail!("not a number"),
|
_ => bail!("not a number"),
|
||||||
|
|
@ -241,10 +485,17 @@ macro_rules! impl_into_number {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_into_integer!(usize, as_usize);
|
impl_into_integer!(i8);
|
||||||
impl_into_integer!(isize, as_isize);
|
impl_into_integer!(i16);
|
||||||
impl_into_integer!(i16, as_i16);
|
impl_into_integer!(i32);
|
||||||
impl_into_integer!(i32, as_i32);
|
impl_into_integer!(i64);
|
||||||
impl_into_integer!(crate::Id, as_id);
|
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)
|
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>
|
pub fn take_seq<T>(&mut self) -> Vec<T>
|
||||||
where
|
where
|
||||||
T: TryFrom<Data>,
|
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>
|
pub fn take_seq<'a, T>(&mut self) -> Vec<T>
|
||||||
where
|
where
|
||||||
T: TryFrom<Data> + TryFrom<&'a Data>,
|
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 len(self) -> usize { self.as_encoded_bytes().len() }
|
||||||
|
|
||||||
|
fn parent(self) -> Option<Self>;
|
||||||
|
|
||||||
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
||||||
where
|
where
|
||||||
T: AsStrandView<'a, Self::Strand<'a>>;
|
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())
|
self.join(path.as_strand_view())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parent(self) -> Option<Self> { self.parent() }
|
||||||
|
|
||||||
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
||||||
where
|
where
|
||||||
T: AsStrandView<'a, Self::Strand<'a>>,
|
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())
|
self.join(path.as_strand_view())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parent(self) -> Option<Self> { self.parent() }
|
||||||
|
|
||||||
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
||||||
where
|
where
|
||||||
T: AsStrandView<'a, Self::Strand<'a>>,
|
T: AsStrandView<'a, Self::Strand<'a>>,
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ where
|
||||||
let old = self.inner.len();
|
let old = self.inner.len();
|
||||||
self.mutate(|path| path.set_file_name(name));
|
self.mutate(|path| path.set_file_name(name));
|
||||||
|
|
||||||
let new = self.len();
|
let new = self.inner.len();
|
||||||
if new == old {
|
if new == old {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -215,6 +215,9 @@ where
|
||||||
loc
|
loc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn parent(&self) -> Option<P::Borrowed<'_>> { self.as_loc().parent() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mutate<T, F: FnOnce(&mut P) -> T>(&mut self, f: F) -> T {
|
fn mutate<T, F: FnOnce(&mut P) -> T>(&mut self, f: F) -> T {
|
||||||
let mut inner = mem::take(&mut self.inner);
|
let mut inner = mem::take(&mut self.inner);
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,64 @@ impl<'p, P> Loc<'p, P>
|
||||||
where
|
where
|
||||||
P: LocAble<'p> + LocAbleImpl<'p>,
|
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
|
pub fn new<'a, T, S>(path: T, base: S, trail: S) -> Self
|
||||||
where
|
where
|
||||||
T: AsPathView<'p, P>,
|
T: AsPathView<'p, P>,
|
||||||
|
|
@ -84,6 +142,67 @@ where
|
||||||
loc
|
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>
|
pub fn with<T>(path: T, uri: usize, urn: usize) -> Result<Self>
|
||||||
where
|
where
|
||||||
T: AsPathView<'p, P>,
|
T: AsPathView<'p, P>,
|
||||||
|
|
@ -116,30 +235,6 @@ where
|
||||||
Ok(loc)
|
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
|
pub fn zeroed<T>(path: T) -> Self
|
||||||
where
|
where
|
||||||
T: AsPathView<'p, P>,
|
T: AsPathView<'p, P>,
|
||||||
|
|
@ -148,94 +243,4 @@ where
|
||||||
(loc.uri, loc.urn) = (0, 0);
|
(loc.uri, loc.urn) = (0, 0);
|
||||||
loc
|
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};
|
use crate::path::{AsPath, PathBufDyn, PathDyn, PathKind};
|
||||||
|
|
||||||
// --- PathCow
|
// --- PathCow
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PathCow<'a> {
|
pub enum PathCow<'a> {
|
||||||
Borrowed(PathDyn<'a>),
|
Borrowed(PathDyn<'a>),
|
||||||
Owned(PathBufDyn),
|
Owned(PathBufDyn),
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,8 @@ impl<'p> PathDyn<'p> {
|
||||||
|
|
||||||
pub fn parent(self) -> Option<Self> {
|
pub fn parent(self) -> Option<Self> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Self::Os(p) => Self::Os(p.parent()?),
|
Self::Os(p) => Self::Os(p.parent().filter(|p| !p.as_os_str().is_empty())?),
|
||||||
Self::Unix(p) => Self::Unix(p.parent()?),
|
Self::Unix(p) => Self::Unix(p.parent().filter(|p| !p.as_bytes().is_empty())?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,16 @@ pub enum Source {
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|
||||||
Key,
|
Key,
|
||||||
Ind,
|
|
||||||
|
|
||||||
Emit,
|
Emit,
|
||||||
EmitInd,
|
|
||||||
|
|
||||||
Relay,
|
Relay,
|
||||||
RelayInd,
|
|
||||||
|
Ind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_key(self) -> bool { self == Self::Key }
|
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() }
|
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 {
|
impl PartialEq<Url<'_>> for UrlBuf {
|
||||||
fn eq(&self, other: &Url) -> bool { self.as_url() == *other }
|
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 }
|
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
|
// --- Hash
|
||||||
impl Hash for UrlBuf {
|
impl Hash for UrlBuf {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) { self.as_url().hash(state) }
|
fn hash<H: Hasher>(&self, state: &mut H) { self.as_url().hash(state) }
|
||||||
|
|
@ -293,7 +305,7 @@ mod tests {
|
||||||
("sftp://remote//", None),
|
("sftp://remote//", None),
|
||||||
// Relative
|
// Relative
|
||||||
("search://kw:2:2/a/b", Some("search://kw:1:1/a")),
|
("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),
|
("search://kw/", None),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue