mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Adding Everything (es) search integration for Windows Users Only
* Integrated Everything CLI (es/esfind) as a new search backend in Yazi. Added Windows-only runtime check to ensure the feature is available only for Windows users. * Updated search actor, parser, plugin, and keymap to support the new "es" search option. * Improved error handling and user feedback for unsupported platforms and missing CLI. * Ensured correct path handling for search results to enable preview and open actions. * Updated documentation and spell-check word list for the new search tool.
This commit is contained in:
parent
9c1f303f2c
commit
e774044cad
10 changed files with 171 additions and 41 deletions
1
buildHere.bat
Normal file
1
buildHere.bat
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
cargo build --profile release-windows --locked
|
||||||
|
|
@ -6,7 +6,10 @@ 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::{SearchOpt, SearchOptVia},
|
||||||
|
};
|
||||||
use yazi_plugin::external;
|
use yazi_plugin::external;
|
||||||
use yazi_proxy::{InputProxy, MgrProxy};
|
use yazi_proxy::{InputProxy, MgrProxy};
|
||||||
use yazi_shared::event::Data;
|
use yazi_shared::event::Data;
|
||||||
|
|
@ -75,6 +78,12 @@ impl Actor for SearchDo {
|
||||||
subject: opt.subject.into_owned(),
|
subject: opt.subject.into_owned(),
|
||||||
args: opt.args,
|
args: opt.args,
|
||||||
}),
|
}),
|
||||||
|
SearchOptVia::Es => external::es(external::EsOpt {
|
||||||
|
cwd: cwd.clone(),
|
||||||
|
hidden,
|
||||||
|
subject: opt.subject.into_owned(),
|
||||||
|
args: opt.args,
|
||||||
|
}),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(5000, Duration::from_millis(500));
|
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(5000, Duration::from_millis(500));
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ impl Actions {
|
||||||
writeln!(s, " fzf : {}", Self::process_output("fzf", "--version"))?;
|
writeln!(s, " fzf : {}", Self::process_output("fzf", "--version"))?;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
writeln!(s, " fd/fdfind : {} / {}", Self::process_output("fd", "--version"), Self::process_output("fdfind", "--version"))?;
|
writeln!(s, " fd/fdfind : {} / {}", Self::process_output("fd", "--version"), Self::process_output("fdfind", "--version"))?;
|
||||||
|
writeln!(s, " es : {}", Self::process_output("es", "--version"))?;
|
||||||
writeln!(s, " rg : {}", Self::process_output("rg", "--version"))?;
|
writeln!(s, " rg : {}", Self::process_output("rg", "--version"))?;
|
||||||
writeln!(s, " chafa : {}", Self::process_output("chafa", "--version"))?;
|
writeln!(s, " chafa : {}", Self::process_output("chafa", "--version"))?;
|
||||||
writeln!(s, " zoxide : {}", Self::process_output("zoxide", "--version"))?;
|
writeln!(s, " zoxide : {}", Self::process_output("zoxide", "--version"))?;
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ keymap = [
|
||||||
{ on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
|
{ on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
|
||||||
{ on = "s", run = "search --via=fd", desc = "Search files by name via fd" },
|
{ on = "s", run = "search --via=fd", desc = "Search files by name via fd" },
|
||||||
{ on = "S", run = "search --via=rg", desc = "Search files by content via ripgrep" },
|
{ on = "S", run = "search --via=rg", desc = "Search files by content via ripgrep" },
|
||||||
|
{ on = "E", run = "search --via=es", desc = "Search files by content via EveryThing" },
|
||||||
{ on = "<C-s>", run = "escape --search", desc = "Cancel the ongoing search" },
|
{ on = "<C-s>", run = "escape --search", desc = "Cancel the ongoing search" },
|
||||||
{ on = "z", run = "plugin fzf", desc = "Jump to a file/directory via fzf" },
|
{ on = "z", run = "plugin fzf", desc = "Jump to a file/directory via fzf" },
|
||||||
{ on = "Z", run = "plugin zoxide", desc = "Jump to a directory via zoxide" },
|
{ on = "Z", run = "plugin zoxide", desc = "Jump to a directory via zoxide" },
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ impl Cha {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_dummy(_url: &Url, ft: Option<FileType>) -> Self {
|
pub fn from_dummy(_url: &Url, ft: Option<FileType>) -> Self {
|
||||||
let mut me = ft.map(Self::from_half_ft).unwrap_or_default();
|
let me = ft.map(Self::from_half_ft).unwrap_or_default();
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if _url.urn().is_hidden() {
|
if _url.urn().is_hidden() {
|
||||||
me.kind |= ChaKind::HIDDEN;
|
me.kind |= ChaKind::HIDDEN;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ impl TryFrom<CmdCow> for SearchOpt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoLua for &SearchOpt {
|
impl IntoLua for &SearchOpt {
|
||||||
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> {
|
||||||
|
Err("unsupported".into_lua_err())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Via
|
// Via
|
||||||
|
|
@ -45,6 +47,7 @@ pub enum SearchOptVia {
|
||||||
Rg,
|
Rg,
|
||||||
Rga,
|
Rga,
|
||||||
Fd,
|
Fd,
|
||||||
|
Es,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for SearchOptVia {
|
impl From<&str> for SearchOptVia {
|
||||||
|
|
@ -52,6 +55,7 @@ impl From<&str> for SearchOptVia {
|
||||||
match value {
|
match value {
|
||||||
"rg" => Self::Rg,
|
"rg" => Self::Rg,
|
||||||
"rga" => Self::Rga,
|
"rga" => Self::Rga,
|
||||||
|
"es" => Self::Es,
|
||||||
_ => Self::Fd,
|
_ => Self::Fd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63,6 +67,7 @@ impl SearchOptVia {
|
||||||
Self::Rg => "rg",
|
Self::Rg => "rg",
|
||||||
Self::Rga => "rga",
|
Self::Rga => "rga",
|
||||||
Self::Fd => "fd",
|
Self::Fd => "fd",
|
||||||
|
Self::Es => "es",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
62
yazi-plugin/src/external/es.rs
vendored
Normal file
62
yazi-plugin/src/external/es.rs
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Stdio;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncBufReadExt, BufReader},
|
||||||
|
process::{Child, Command},
|
||||||
|
sync::mpsc::{self, UnboundedReceiver},
|
||||||
|
};
|
||||||
|
use yazi_fs::File;
|
||||||
|
use yazi_shared::url::Url;
|
||||||
|
|
||||||
|
pub struct EsOpt {
|
||||||
|
pub cwd: Url,
|
||||||
|
pub hidden: bool,
|
||||||
|
pub subject: String,
|
||||||
|
pub args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn es(opt: EsOpt) -> Result<UnboundedReceiver<File>> {
|
||||||
|
let mut child = spawn("es", &opt)?;
|
||||||
|
|
||||||
|
let mut it = BufReader::new(child.stdout.take().unwrap()).lines();
|
||||||
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Ok(Some(line)) = it.next_line().await {
|
||||||
|
if let Ok(file) = File::new(Path::new(&line).into()).await {
|
||||||
|
tx.send(file).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child.wait().await.ok();
|
||||||
|
});
|
||||||
|
Ok(rx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn(program: &str, opt: &EsOpt) -> std::io::Result<Child> {
|
||||||
|
if !cfg!(windows) {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Unsupported,
|
||||||
|
"Sorry, EveryThing Search is only avaiable for Windows Users!",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(path) = opt.cwd.as_path() else {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::InvalidInput,
|
||||||
|
"Check if you have EveryThing CLI installed!",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Command::new(program)
|
||||||
|
.arg("-path")
|
||||||
|
.arg(path)
|
||||||
|
// .arg(if opt.hidden { "/ah" } else { "/a-h" })
|
||||||
|
// .args(&opt.args)
|
||||||
|
.arg("-regex")
|
||||||
|
.arg(&opt.subject)
|
||||||
|
.kill_on_drop(true)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.spawn()
|
||||||
|
}
|
||||||
2
yazi-plugin/src/external/mod.rs
vendored
2
yazi-plugin/src/external/mod.rs
vendored
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(fd highlighter rg rga);
|
yazi_macro::mod_flat!(fd highlighter rg rga es);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, hash::BuildHasher, ops::Deref, path::{Path, PathBuf}};
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
hash::BuildHasher,
|
||||||
|
ops::Deref,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
use percent_encoding::percent_decode;
|
use percent_encoding::percent_decode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::UrnBuf;
|
use super::UrnBuf;
|
||||||
use crate::{IntoOsStr, url::{Components, Display, Loc, Scheme}};
|
use crate::{
|
||||||
|
IntoOsStr,
|
||||||
|
url::{Components, Display, Loc, Scheme},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Default, Eq, Ord, PartialOrd, PartialEq, Hash)]
|
#[derive(Clone, Default, Eq, Ord, PartialOrd, PartialEq, Hash)]
|
||||||
pub struct Url {
|
pub struct Url {
|
||||||
|
|
@ -15,23 +25,33 @@ pub struct Url {
|
||||||
impl Deref for Url {
|
impl Deref for Url {
|
||||||
type Target = Loc;
|
type Target = Loc;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.loc }
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.loc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Loc> for Url {
|
impl From<Loc> for Url {
|
||||||
fn from(loc: Loc) -> Self { Self { loc, scheme: Scheme::Regular } }
|
fn from(loc: Loc) -> Self {
|
||||||
|
Self { loc, scheme: Scheme::Regular }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PathBuf> for Url {
|
impl From<PathBuf> for Url {
|
||||||
fn from(path: PathBuf) -> Self { Loc::from(path).into() }
|
fn from(path: PathBuf) -> Self {
|
||||||
|
Loc::from(path).into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&PathBuf> for Url {
|
impl From<&PathBuf> for Url {
|
||||||
fn from(path: &PathBuf) -> Self { path.to_owned().into() }
|
fn from(path: &PathBuf) -> Self {
|
||||||
|
path.to_owned().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Path> for Url {
|
impl From<&Path> for Url {
|
||||||
fn from(path: &Path) -> Self { path.to_path_buf().into() }
|
fn from(path: &Path) -> Self {
|
||||||
|
path.to_path_buf().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for Url {
|
impl TryFrom<&[u8]> for Url {
|
||||||
|
|
@ -53,34 +73,48 @@ impl TryFrom<&[u8]> for Url {
|
||||||
impl TryFrom<&str> for Url {
|
impl TryFrom<&str> for Url {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> { value.as_bytes().try_into() }
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
value.as_bytes().try_into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for Url {
|
impl TryFrom<String> for Url {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> { value.as_bytes().try_into() }
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
value.as_bytes().try_into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Url> for Url {
|
impl AsRef<Url> for Url {
|
||||||
fn as_ref(&self) -> &Url { self }
|
fn as_ref(&self) -> &Url {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: remove
|
// FIXME: remove
|
||||||
impl AsRef<Path> for Url {
|
impl AsRef<Path> for Url {
|
||||||
fn as_ref(&self) -> &Path { &self.loc }
|
fn as_ref(&self) -> &Path {
|
||||||
|
&self.loc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Url> for Cow<'a, Url> {
|
impl<'a> From<&'a Url> for Cow<'a, Url> {
|
||||||
fn from(url: &'a Url) -> Self { Cow::Borrowed(url) }
|
fn from(url: &'a Url) -> Self {
|
||||||
|
Cow::Borrowed(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Url> for Cow<'_, Url> {
|
impl From<Url> for Cow<'_, Url> {
|
||||||
fn from(url: Url) -> Self { Cow::Owned(url) }
|
fn from(url: Url) -> Self {
|
||||||
|
Cow::Owned(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Cow<'_, Url>> for Url {
|
impl From<Cow<'_, Url>> for Url {
|
||||||
fn from(url: Cow<'_, Url>) -> Self { url.into_owned() }
|
fn from(url: Cow<'_, Url>) -> Self {
|
||||||
|
url.into_owned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Url {
|
impl Url {
|
||||||
|
|
@ -105,16 +139,16 @@ impl Url {
|
||||||
Scheme::SearchItem => {
|
Scheme::SearchItem => {
|
||||||
Self { loc: Loc::with(self.loc.base(), self.loc.join(path)), scheme: Scheme::SearchItem }
|
Self { loc: Loc::with(self.loc.base(), self.loc.join(path)), scheme: Scheme::SearchItem }
|
||||||
}
|
}
|
||||||
Scheme::Archive(_) => {
|
Scheme::Archive(_) => Self { loc: self.loc.join(path).into(), scheme: self.scheme.clone() },
|
||||||
Self { loc: self.loc.join(path).into(), scheme: self.scheme.clone() }
|
|
||||||
}
|
|
||||||
Scheme::Sftp(_) => Self { loc: self.loc.join(path).into(), scheme: self.scheme.clone() },
|
Scheme::Sftp(_) => Self { loc: self.loc.join(path).into(), scheme: self.scheme.clone() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: check usages
|
// FIXME: check usages
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn components(&self) -> Components<'_> { Components::new(self) }
|
pub fn components(&self) -> Components<'_> {
|
||||||
|
Components::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn covariant(&self, other: &Self) -> bool {
|
pub fn covariant(&self, other: &Self) -> bool {
|
||||||
|
|
@ -122,10 +156,14 @@ impl Url {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn display(&self) -> Display<'_> { Display::new(self) }
|
pub fn display(&self) -> Display<'_> {
|
||||||
|
Display::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
pub fn os_str(&self) -> Cow<'_, OsStr> {
|
||||||
|
self.components().os_str()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parent_url(&self) -> Option<Url> {
|
pub fn parent_url(&self) -> Option<Url> {
|
||||||
let parent = self.loc.parent()?;
|
let parent = self.loc.parent()?;
|
||||||
|
|
@ -151,10 +189,7 @@ impl Url {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Self {
|
Some(Self { loc: self.loc.strip_prefix(&base.loc).ok()?.into(), scheme: self.scheme.clone() })
|
||||||
loc: self.loc.strip_prefix(&base.loc).ok()?.into(),
|
|
||||||
scheme: self.scheme.clone(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -163,13 +198,19 @@ impl Url {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_name(&mut self, name: impl AsRef<OsStr>) { self.loc.set_name(name); }
|
pub fn set_name(&mut self, name: impl AsRef<OsStr>) {
|
||||||
|
self.loc.set_name(name);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) }
|
pub fn pair(&self) -> Option<(Self, UrnBuf)> {
|
||||||
|
Some((self.parent_url()?, self.loc.urn_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
|
pub fn hash_u64(&self) -> u64 {
|
||||||
|
foldhash::fast::FixedState::default().hash_one(self)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rebase(&self, parent: &Path) -> Self {
|
pub fn rebase(&self, parent: &Path) -> Self {
|
||||||
|
|
@ -181,10 +222,14 @@ impl Url {
|
||||||
impl Url {
|
impl Url {
|
||||||
// --- Regular
|
// --- Regular
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_regular(&self) -> bool { self.scheme == Scheme::Regular }
|
pub fn is_regular(&self) -> bool {
|
||||||
|
self.scheme == Scheme::Regular
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_regular(&self) -> Self { Self { loc: self.loc.clone(), scheme: Scheme::Regular } }
|
pub fn to_regular(&self) -> Self {
|
||||||
|
Self { loc: self.loc.clone(), scheme: Scheme::Regular }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_regular(mut self) -> Self {
|
pub fn into_regular(mut self) -> Self {
|
||||||
|
|
@ -194,7 +239,9 @@ impl Url {
|
||||||
|
|
||||||
// --- Search
|
// --- Search
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_search(&self) -> bool { matches!(self.scheme, Scheme::Search(_)) }
|
pub fn is_search(&self) -> bool {
|
||||||
|
matches!(self.scheme, Scheme::Search(_))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_search(&self, frag: impl AsRef<str>) -> Self {
|
pub fn to_search(&self, frag: impl AsRef<str>) -> Self {
|
||||||
|
|
@ -209,11 +256,15 @@ impl Url {
|
||||||
|
|
||||||
// --- Archive
|
// --- Archive
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_archive(&self) -> bool { matches!(self.scheme, Scheme::Archive(_)) }
|
pub fn is_archive(&self) -> bool {
|
||||||
|
matches!(self.scheme, Scheme::Archive(_))
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: remove
|
// FIXME: remove
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_path(self) -> PathBuf { self.loc.into_path() }
|
pub fn into_path(self) -> PathBuf {
|
||||||
|
self.loc.into_path()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Url {
|
impl Debug for Url {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue