mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
perf: copy-on-write on command data & avoid converting primitive types to strings thereby allocating memory (#2862)
This commit is contained in:
parent
0752b53298
commit
414d46fd16
22 changed files with 131 additions and 104 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -1025,7 +1025,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1470,7 +1470,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
|
|
@ -2992,9 +2992,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
|
|
@ -3318,9 +3318,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.10"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use yazi_shared::{event::CmdCow, url::Url};
|
|||
use crate::{mgr::{LINKED, Mgr}, tasks::Tasks};
|
||||
|
||||
pub struct Opt {
|
||||
updates: HashMap<Cow<'static, str>, String>,
|
||||
updates: HashMap<Cow<'static, str>, Cow<'static, str>>,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for Opt {
|
||||
|
|
@ -28,13 +28,13 @@ impl Mgr {
|
|||
let updates = opt
|
||||
.updates
|
||||
.into_iter()
|
||||
.map(|(url, mime)| (Url::from(url.into_owned()), mime))
|
||||
.map(|(url, mime)| (Url::from(url.as_ref()), mime))
|
||||
.filter(|(url, mime)| self.mimetype.by_url(url) != Some(mime))
|
||||
.fold(HashMap::new(), |mut map, (u, m)| {
|
||||
for u in linked.from_file(&u) {
|
||||
map.insert(u, m.clone());
|
||||
map.insert(u, m.to_string());
|
||||
}
|
||||
map.insert(u, m);
|
||||
map.insert(u, m.into_owned());
|
||||
map
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ impl Notify {
|
|||
|
||||
if self.messages.iter().all(|m| m != &msg) {
|
||||
self.messages.push(msg);
|
||||
emit!(Call(Cmd::args("app:update_notify", &[0])));
|
||||
emit!(Call(Cmd::args("app:update_notify", [0])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl Notify {
|
|||
|
||||
self.tick_handle = Some(tokio::spawn(async move {
|
||||
tokio::time::sleep(interval).await;
|
||||
emit!(Call(Cmd::args("app:update_notify", &[interval.as_secs_f64()])));
|
||||
emit!(Call(Cmd::args("app:update_notify", [interval.as_secs_f64()])));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ impl Tab {
|
|||
let (Ok(s) | Err(InputError::Typed(s))) = result else { continue };
|
||||
|
||||
emit!(Call(
|
||||
Cmd::args("mgr:filter_do", &[s])
|
||||
.with_bool("smart", opt.case == FilterCase::Smart)
|
||||
.with_bool("insensitive", opt.case == FilterCase::Insensitive)
|
||||
.with_bool("done", done)
|
||||
Cmd::args("mgr:filter_do", [s])
|
||||
.with("smart", opt.case == FilterCase::Smart)
|
||||
.with("insensitive", opt.case == FilterCase::Insensitive)
|
||||
.with("done", done)
|
||||
));
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ impl Tab {
|
|||
|
||||
while let Some(Ok(s)) | Some(Err(InputError::Typed(s))) = rx.next().await {
|
||||
emit!(Call(
|
||||
Cmd::args("mgr:find_do", &[s])
|
||||
.with_bool("previous", opt.prev)
|
||||
.with_bool("smart", opt.case == FilterCase::Smart)
|
||||
.with_bool("insensitive", opt.case == FilterCase::Insensitive)
|
||||
Cmd::args("mgr:find_do", [s])
|
||||
.with("previous", opt.prev)
|
||||
.with("smart", opt.case == FilterCase::Smart)
|
||||
.with("insensitive", opt.case == FilterCase::Insensitive)
|
||||
));
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ impl Tab {
|
|||
handle.abort();
|
||||
}
|
||||
|
||||
let mut input = InputProxy::show(InputCfg::search(opt.via.as_ref()).with_value(&*opt.subject));
|
||||
let mut input =
|
||||
InputProxy::show(InputCfg::search(opt.via.into_str()).with_value(&*opt.subject));
|
||||
tokio::spawn(async move {
|
||||
if let Some(Ok(subject)) = input.recv().await {
|
||||
opt.subject = Cow::Owned(subject);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ impl Sendable {
|
|||
Value::Number(n) => Data::Number(n),
|
||||
Value::String(b) => {
|
||||
if let Ok(s) = b.to_str() {
|
||||
Data::String(s.to_owned())
|
||||
Data::String(s.to_owned().into())
|
||||
} else {
|
||||
Data::Bytes(b.as_bytes().to_owned())
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ impl Sendable {
|
|||
Data::Boolean(b) => Value::Boolean(*b),
|
||||
Data::Integer(i) => Value::Integer(*i),
|
||||
Data::Number(n) => Value::Number(*n),
|
||||
Data::String(s) => Value::String(lua.create_string(s)?),
|
||||
Data::String(s) => Value::String(lua.create_string(s.as_ref())?),
|
||||
Data::List(l) => {
|
||||
let mut vec = Vec::with_capacity(l.len());
|
||||
for v in l {
|
||||
|
|
|
|||
|
|
@ -25,24 +25,19 @@ impl App {
|
|||
app.render();
|
||||
|
||||
let mut events = Vec::with_capacity(50);
|
||||
let (mut timeout, mut last_render) = (None, Instant::now());
|
||||
macro_rules! drain_events {
|
||||
() => {
|
||||
for event in events.drain(..) {
|
||||
app.dispatch(event)?;
|
||||
try_render!();
|
||||
}
|
||||
};
|
||||
}
|
||||
if !NEED_RENDER.load(Ordering::Relaxed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (mut timeout, mut last_render) = (None, Instant::now());
|
||||
macro_rules! try_render {
|
||||
() => {
|
||||
if NEED_RENDER.load(Ordering::Relaxed) {
|
||||
if let Some(sub) = Duration::from_millis(10).checked_sub(last_render.elapsed()) {
|
||||
timeout = Some(sleep(sub));
|
||||
} else {
|
||||
timeout = Duration::from_millis(10).checked_sub(last_render.elapsed());
|
||||
if timeout.is_none() {
|
||||
app.render();
|
||||
(timeout, last_render) = (None, Instant::now());
|
||||
last_render = Instant::now();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -51,7 +46,7 @@ impl App {
|
|||
loop {
|
||||
if let Some(t) = timeout.take() {
|
||||
select! {
|
||||
_ = t => {
|
||||
_ = sleep(t) => {
|
||||
app.render();
|
||||
last_render = Instant::now();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ impl<'a> Executor<'a> {
|
|||
on!(TABS, switch);
|
||||
on!(TABS, swap);
|
||||
|
||||
match cmd.name.as_str() {
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
"help" => self.app.cx.help.toggle(Layer::Mgr),
|
||||
// Plugin
|
||||
|
|
@ -172,7 +172,7 @@ impl<'a> Executor<'a> {
|
|||
on!(open_with);
|
||||
on!(process_exec);
|
||||
|
||||
match cmd.name.as_str() {
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
"help" => self.app.cx.help.toggle(Layer::Tasks),
|
||||
// Plugin
|
||||
|
|
@ -195,7 +195,7 @@ impl<'a> Executor<'a> {
|
|||
on!(swipe);
|
||||
on!(copy);
|
||||
|
||||
match cmd.name.as_str() {
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
"help" => self.app.cx.help.toggle(Layer::Spot),
|
||||
// Plugin
|
||||
|
|
@ -217,7 +217,7 @@ impl<'a> Executor<'a> {
|
|||
on!(close);
|
||||
on!(arrow);
|
||||
|
||||
match cmd.name.as_str() {
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
"help" => self.app.cx.help.toggle(Layer::Pick),
|
||||
// Plugin
|
||||
|
|
@ -241,7 +241,7 @@ impl<'a> Executor<'a> {
|
|||
|
||||
match self.app.cx.input.mode() {
|
||||
InputMode::Normal => {
|
||||
match cmd.name.as_str() {
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
"help" => return self.app.cx.help.toggle(Layer::Input),
|
||||
// Plugin
|
||||
|
|
@ -249,7 +249,7 @@ impl<'a> Executor<'a> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
InputMode::Insert => match cmd.name.as_str() {
|
||||
InputMode::Insert => match cmd.name.as_ref() {
|
||||
"complete" if cmd.bool("trigger") => return self.app.cx.cmp.trigger(cmd),
|
||||
_ => {}
|
||||
},
|
||||
|
|
@ -286,7 +286,7 @@ impl<'a> Executor<'a> {
|
|||
on!(arrow);
|
||||
on!(filter);
|
||||
|
||||
match cmd.name.as_str() {
|
||||
match cmd.name.as_ref() {
|
||||
"close" => self.app.cx.help.toggle(Layer::Help),
|
||||
// Plugin
|
||||
"plugin" => self.app.plugin(cmd),
|
||||
|
|
@ -308,7 +308,7 @@ impl<'a> Executor<'a> {
|
|||
on!(close);
|
||||
on!(arrow);
|
||||
|
||||
match cmd.name.as_str() {
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
"help" => self.app.cx.help.toggle(Layer::Cmp),
|
||||
// Plugin
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ impl Fetcher {
|
|||
|
||||
impl UserData for Fetcher {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ impl Spotter {
|
|||
|
||||
impl UserData for Spotter {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ impl Preloader {
|
|||
|
||||
impl UserData for Preloader {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +109,6 @@ impl Previewer {
|
|||
|
||||
impl UserData for Previewer {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(&me.inner.run.name));
|
||||
cached_field!(fields, cmd, |lua, me| lua.create_string(me.inner.run.name.as_ref()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub fn peek(
|
|||
skip: usize,
|
||||
) -> Option<CancellationToken> {
|
||||
let ct = CancellationToken::new();
|
||||
if let Some(c) = LOADER.read().get(&cmd.name) {
|
||||
if let Some(c) = LOADER.read().get(cmd.name.as_ref()) {
|
||||
if c.sync_peek {
|
||||
peek_sync(cmd, file, mime, skip);
|
||||
} else {
|
||||
|
|
@ -59,7 +59,7 @@ fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Cow<'static, str>, sk
|
|||
plugin.call_method("peek", job)
|
||||
});
|
||||
|
||||
AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
|
||||
AppProxy::plugin(PluginOpt::new_callback(cmd.name.as_ref(), cb));
|
||||
}
|
||||
|
||||
fn peek_async(
|
||||
|
|
|
|||
|
|
@ -16,5 +16,5 @@ pub fn seek_sync(cmd: &'static Cmd, file: yazi_fs::File, units: i16) {
|
|||
plugin.call_method("seek", job)
|
||||
});
|
||||
|
||||
AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
|
||||
AppProxy::plugin(PluginOpt::new_callback(cmd.name.as_ref(), cb));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ impl Utils {
|
|||
}
|
||||
|
||||
pub(super) fn emit(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, (name, args): (mlua::String, Table)| {
|
||||
let mut cmd = Cmd::new_or(&name.to_str()?, Layer::Mgr)?;
|
||||
lua.create_function(|_, (name, args): (String, Table)| {
|
||||
let mut cmd = Cmd::new_or(name, Layer::Mgr)?;
|
||||
cmd.args = Sendable::table_to_args(args)?;
|
||||
Ok(emit!(Call(cmd)))
|
||||
})
|
||||
|
|
@ -23,7 +23,11 @@ impl Utils {
|
|||
|
||||
pub(super) fn mgr_emit(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, (name, args): (String, Table)| {
|
||||
emit!(Call(Cmd { name, args: Sendable::table_to_args(args)?, layer: Layer::Mgr }));
|
||||
emit!(Call(Cmd {
|
||||
name: name.into(),
|
||||
args: Sendable::table_to_args(args)?,
|
||||
layer: Layer::Mgr,
|
||||
}));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ impl Utils {
|
|||
let cand = cand?;
|
||||
cands.push(Chord {
|
||||
on: Self::parse_keys(cand.raw_get("on")?)?,
|
||||
run: vec![Cmd::args("which:callback", &[i]).with_any("tx", tx.clone())],
|
||||
run: vec![Cmd::args("which:callback", [i]).with_any("tx", tx.clone())],
|
||||
desc: cand.raw_get("desc").ok(),
|
||||
r#for: None,
|
||||
});
|
||||
|
|
@ -31,7 +31,7 @@ impl Utils {
|
|||
emit!(Call(
|
||||
Cmd::new("which:show")
|
||||
.with_any("candidates", cands)
|
||||
.with_bool("silent", t.raw_get("silent").unwrap_or_default())
|
||||
.with("silent", t.raw_get::<bool>("silent").unwrap_or_default())
|
||||
));
|
||||
|
||||
Ok(rx.recv().await.map(|idx| idx + 1))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ impl CmpProxy {
|
|||
|
||||
#[inline]
|
||||
pub fn trigger(word: &str, ticket: Id) {
|
||||
emit!(Call(Cmd::args("cmp:trigger", &[word]).with("ticket", ticket)));
|
||||
emit!(Call(Cmd::args("cmp:trigger", [word]).with("ticket", ticket)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl MgrProxy {
|
|||
|
||||
#[inline]
|
||||
pub fn peek(force: bool) {
|
||||
emit!(Call(Cmd::new("mgr:peek").with_bool("force", force)));
|
||||
emit!(Call(Cmd::new("mgr:peek").with("force", force)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -34,7 +34,7 @@ impl MgrProxy {
|
|||
#[inline]
|
||||
pub fn remove_do(targets: Vec<Url>, permanently: bool) {
|
||||
emit!(Call(
|
||||
Cmd::new("mgr:remove_do").with_bool("permanently", permanently).with_any("targets", targets)
|
||||
Cmd::new("mgr:remove_do").with("permanently", permanently).with_any("targets", targets)
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +50,6 @@ impl MgrProxy {
|
|||
|
||||
#[inline]
|
||||
pub fn update_paged_by(page: usize, only_if: &Url) {
|
||||
emit!(Call(Cmd::args("mgr:update_paged", &[page]).with_any("only-if", only_if.clone())));
|
||||
emit!(Call(Cmd::args("mgr:update_paged", [page]).with_any("only-if", only_if.clone())));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ impl TryFrom<CmdCow> for SearchOpt {
|
|||
}
|
||||
|
||||
// Via
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SearchOptVia {
|
||||
Rg,
|
||||
Rga,
|
||||
|
|
@ -50,8 +50,8 @@ impl From<&str> for SearchOptVia {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for SearchOptVia {
|
||||
fn as_ref(&self) -> &str {
|
||||
impl SearchOptVia {
|
||||
pub fn into_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Rg => "rg",
|
||||
Self::Rga => "rga",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use yazi_macro::emit;
|
||||
use yazi_shared::{event::Cmd, url::Url};
|
||||
|
||||
|
|
@ -8,25 +10,25 @@ pub struct TabProxy;
|
|||
impl TabProxy {
|
||||
#[inline]
|
||||
pub fn cd(target: &Url) {
|
||||
emit!(Call(Cmd::args("mgr:cd", &[target])));
|
||||
emit!(Call(Cmd::args("mgr:cd", [target])));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reveal(target: &Url) {
|
||||
emit!(Call(Cmd::args("mgr:reveal", &[target]).with("no-dummy", true)));
|
||||
emit!(Call(Cmd::args("mgr:reveal", [target]).with("no-dummy", true)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn arrow(step: impl AsRef<str>) {
|
||||
emit!(Call(Cmd::args("mgr:arrow", &[step.as_ref()])));
|
||||
pub fn arrow(step: impl Into<Cow<'static, str>>) {
|
||||
emit!(Call(Cmd::args("mgr:arrow", [step.into()])));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn search_do(opt: SearchOpt) {
|
||||
emit!(Call(
|
||||
// TODO: use second positional argument instead of `args` parameter
|
||||
Cmd::args("mgr:search_do", &[opt.subject])
|
||||
.with("via", opt.via.as_ref().to_owned())
|
||||
Cmd::args("mgr:search_do", [opt.subject])
|
||||
.with("via", Cow::Borrowed(opt.via.into_str()))
|
||||
.with("args", opt.args_raw.into_owned())
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{any::Any, borrow::Cow, collections::HashMap, fmt::{self, Display}, str::FromStr};
|
||||
use std::{any::Any, borrow::Cow, collections::HashMap, fmt::{self, Display}, mem, str::FromStr};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use serde::{Deserialize, de};
|
||||
|
|
@ -8,33 +8,48 @@ use crate::{Layer, url::Url};
|
|||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Cmd {
|
||||
pub name: String,
|
||||
pub name: Cow<'static, str>,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
pub layer: Layer,
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
pub fn new(s: &str) -> Self {
|
||||
Self::new_or(s, Default::default())
|
||||
.unwrap_or_else(|_| Self { name: "null".to_owned(), ..Default::default() })
|
||||
pub fn new<N>(name: N) -> Self
|
||||
where
|
||||
N: Into<Cow<'static, str>>,
|
||||
{
|
||||
Self::new_or(name, Default::default())
|
||||
.unwrap_or_else(|_| Self { name: "null".into(), ..Default::default() })
|
||||
}
|
||||
|
||||
pub fn new_or(s: &str, default: Layer) -> Result<Self> {
|
||||
let (layer, name) = match s.split_once(':') {
|
||||
Some((l, n)) => (l.parse()?, n),
|
||||
None => (default, s),
|
||||
pub fn new_or<N>(name: N, default: Layer) -> Result<Self>
|
||||
where
|
||||
N: Into<Cow<'static, str>>,
|
||||
{
|
||||
let cow: Cow<'static, str> = name.into();
|
||||
let (layer, name) = match cow.find(':') {
|
||||
None => (default, cow),
|
||||
Some(i) => (cow[..i].parse()?, match cow {
|
||||
Cow::Borrowed(s) => Cow::Borrowed(&s[i + 1..]),
|
||||
Cow::Owned(mut s) => {
|
||||
s.drain(..i + 1);
|
||||
Cow::Owned(s)
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
Ok(Self { name: name.to_owned(), args: Default::default(), layer })
|
||||
Ok(Self { name, args: Default::default(), layer })
|
||||
}
|
||||
|
||||
pub fn args(name: &str, args: &[impl ToString]) -> Self {
|
||||
pub fn args<N, D, I>(name: N, args: I) -> Self
|
||||
where
|
||||
N: Into<Cow<'static, str>>,
|
||||
D: Into<Data>,
|
||||
I: IntoIterator<Item = D>,
|
||||
{
|
||||
let mut me = Self::new(name);
|
||||
me.args = args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| (DataKey::Integer(i as i64), Data::String(s.to_string())))
|
||||
.collect();
|
||||
me.args =
|
||||
args.into_iter().enumerate().map(|(i, a)| (DataKey::Integer(i as i64), a.into())).collect();
|
||||
me
|
||||
}
|
||||
|
||||
|
|
@ -59,12 +74,6 @@ impl Cmd {
|
|||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_bool(mut self, name: impl Into<DataKey>, state: bool) -> Self {
|
||||
self.args.insert(name.into(), Data::Boolean(state));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_any(mut self, name: impl Into<DataKey>, data: impl Any + Send + Sync) -> Self {
|
||||
self.args.insert(name.into(), Data::Any(Box::new(data)));
|
||||
|
|
@ -107,7 +116,7 @@ impl Cmd {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn take_str(&mut self, name: impl Into<DataKey>) -> Option<String> {
|
||||
pub fn take_str(&mut self, name: impl Into<DataKey>) -> Option<Cow<'static, str>> {
|
||||
if let Some(Data::String(s)) = self.take(name) { Some(s) } else { None }
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +124,7 @@ impl Cmd {
|
|||
pub fn take_first(&mut self) -> Option<Data> { self.take(0) }
|
||||
|
||||
#[inline]
|
||||
pub fn take_first_str(&mut self) -> Option<String> {
|
||||
pub fn take_first_str(&mut self) -> Option<Cow<'static, str>> {
|
||||
if let Some(Data::String(s)) = self.take_first() { Some(s) } else { None }
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +150,7 @@ impl Cmd {
|
|||
.map(|(word, normal)| {
|
||||
let Some(arg) = word.strip_prefix("--").filter(|_| normal) else {
|
||||
i += 1;
|
||||
return Ok((DataKey::Integer(i - obase as i64), Data::String(word)));
|
||||
return Ok((DataKey::Integer(i - obase as i64), Data::String(word.into())));
|
||||
};
|
||||
|
||||
let mut parts = arg.splitn(2, '=');
|
||||
|
|
@ -150,7 +159,7 @@ impl Cmd {
|
|||
};
|
||||
|
||||
let val = if let Some(val) = parts.next() {
|
||||
Data::String(val.to_owned())
|
||||
Data::String(val.to_owned().into())
|
||||
} else {
|
||||
Data::Boolean(true)
|
||||
};
|
||||
|
|
@ -189,12 +198,12 @@ impl FromStr for Cmd {
|
|||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (words, last) = crate::shell::split_unix(s, true)?;
|
||||
let (mut words, last) = crate::shell::split_unix(s, true)?;
|
||||
if words.is_empty() || words[0].is_empty() {
|
||||
bail!("command name cannot be empty");
|
||||
}
|
||||
|
||||
let mut me = Self::new(&words[0]);
|
||||
let mut me = Self::new(mem::take(&mut words[0]));
|
||||
me.args = Cmd::parse_args(words.into_iter().skip(1), last, true)?;
|
||||
Ok(me)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ impl CmdCow {
|
|||
#[inline]
|
||||
pub fn take_str(&mut self, name: impl Into<DataKey>) -> Option<Cow<'static, str>> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take_str(name).map(Cow::Owned),
|
||||
Self::Owned(c) => c.take_str(name),
|
||||
Self::Borrowed(c) => c.str(name).map(Cow::Borrowed),
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ impl CmdCow {
|
|||
#[inline]
|
||||
pub fn take_first_str(&mut self) -> Option<Cow<'static, str>> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take_first_str().map(Cow::Owned),
|
||||
Self::Owned(c) => c.take_first_str(),
|
||||
Self::Borrowed(c) => c.first_str().map(Cow::Borrowed),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub enum Data {
|
|||
Boolean(bool),
|
||||
Integer(i64),
|
||||
Number(f64),
|
||||
String(String),
|
||||
String(Cow<'static, str>),
|
||||
List(Vec<Data>),
|
||||
Dict(HashMap<DataKey, Data>),
|
||||
Id(Id),
|
||||
|
|
@ -56,13 +56,13 @@ impl Data {
|
|||
#[inline]
|
||||
pub fn into_url(self) -> Option<Url> {
|
||||
match self {
|
||||
Data::String(s) => Some(Url::from(s)),
|
||||
Data::String(s) => Some(Url::from(s.as_ref())),
|
||||
Data::Url(u) => Some(u),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_dict_string(self) -> HashMap<Cow<'static, str>, String> {
|
||||
pub fn into_dict_string(self) -> HashMap<Cow<'static, str>, Cow<'static, str>> {
|
||||
let Self::Dict(dict) = self else {
|
||||
return Default::default();
|
||||
};
|
||||
|
|
@ -79,7 +79,7 @@ impl Data {
|
|||
#[inline]
|
||||
pub fn to_url(&self) -> Option<Url> {
|
||||
match self {
|
||||
Self::String(s) => Some(Url::from(s)),
|
||||
Self::String(s) => Some(Url::from(s.as_ref())),
|
||||
Self::Url(u) => Some(u.clone()),
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -90,18 +90,34 @@ impl From<bool> for Data {
|
|||
fn from(value: bool) -> Self { Self::Boolean(value) }
|
||||
}
|
||||
|
||||
impl From<f64> for Data {
|
||||
fn from(value: f64) -> Self { Self::Number(value) }
|
||||
}
|
||||
|
||||
impl From<usize> for Data {
|
||||
fn from(value: usize) -> Self { Self::Id(value.into()) }
|
||||
}
|
||||
|
||||
impl From<String> for Data {
|
||||
fn from(value: String) -> Self { Self::String(value) }
|
||||
fn from(value: String) -> Self { Self::String(Cow::Owned(value)) }
|
||||
}
|
||||
|
||||
impl From<Cow<'static, str>> for Data {
|
||||
fn from(value: Cow<'static, str>) -> Self { Self::String(value) }
|
||||
}
|
||||
|
||||
impl From<Id> for Data {
|
||||
fn from(value: Id) -> Self { Self::Id(value) }
|
||||
}
|
||||
|
||||
impl From<&Url> for Data {
|
||||
fn from(value: &Url) -> Self { Self::Url(value.clone()) }
|
||||
}
|
||||
|
||||
impl From<&str> for Data {
|
||||
fn from(value: &str) -> Self { Self::String(Cow::Owned(value.to_owned())) }
|
||||
}
|
||||
|
||||
// --- Key
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue