mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: new ya emit and ya emit-to subcommands to emit commands to a specified instance for execution (#1979)
This commit is contained in:
parent
91665b7713
commit
20dc0550bc
13 changed files with 192 additions and 66 deletions
|
|
@ -1 +1 @@
|
||||||
{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes","luma"],"language":"en"}
|
{"flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes","luma","obase"],"version":"0.2","language":"en"}
|
||||||
|
|
@ -10,7 +10,7 @@ pub static BOOT: RoCell<Boot> = RoCell::new();
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
ARGS.with(<_>::parse);
|
ARGS.with(<_>::parse);
|
||||||
BOOT.init(From::from(&*ARGS));
|
BOOT.init(<_>::from(&*ARGS));
|
||||||
|
|
||||||
actions::Actions::act(&ARGS);
|
actions::Actions::act(&ARGS);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ tokio = { workspace = true }
|
||||||
toml_edit = "0.22.22"
|
toml_edit = "0.22.22"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
yazi-shared = { path = "../yazi-shared", version = "0.3.3" }
|
||||||
|
|
||||||
# External build dependencies
|
# External build dependencies
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use clap::{Parser, Subcommand, command};
|
use clap::{Parser, Subcommand, command};
|
||||||
|
use yazi_shared::event::Cmd;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "Ya", about, long_about = None)]
|
#[command(name = "Ya", about, long_about = None)]
|
||||||
|
|
@ -16,14 +17,55 @@ pub(super) struct Args {
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub(super) enum Command {
|
pub(super) enum Command {
|
||||||
|
/// Emit a command to be executed by the current instance.
|
||||||
|
Emit(CommandEmit),
|
||||||
|
/// Emit a command to be executed by the specified instance.
|
||||||
|
EmitTo(CommandEmitTo),
|
||||||
|
/// Manage packages.
|
||||||
|
Pack(CommandPack),
|
||||||
/// Publish a message to the current instance.
|
/// Publish a message to the current instance.
|
||||||
Pub(CommandPub),
|
Pub(CommandPub),
|
||||||
/// Publish a message to the specified instance.
|
/// Publish a message to the specified instance.
|
||||||
PubTo(CommandPubTo),
|
PubTo(CommandPubTo),
|
||||||
/// Subscribe to messages from all remote instances.
|
/// Subscribe to messages from all remote instances.
|
||||||
Sub(CommandSub),
|
Sub(CommandSub),
|
||||||
/// Manage packages.
|
}
|
||||||
Pack(CommandPack),
|
|
||||||
|
#[derive(clap::Args)]
|
||||||
|
pub(super) struct CommandEmit {
|
||||||
|
/// The name of the command.
|
||||||
|
pub(super) name: String,
|
||||||
|
/// The arguments of the command.
|
||||||
|
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
|
||||||
|
pub(super) args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(clap::Args)]
|
||||||
|
pub(super) struct CommandEmitTo {
|
||||||
|
/// The receiver ID.
|
||||||
|
pub(super) receiver: u64,
|
||||||
|
/// The name of the command.
|
||||||
|
pub(super) name: String,
|
||||||
|
/// The arguments of the command.
|
||||||
|
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
|
||||||
|
pub(super) args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(clap::Args)]
|
||||||
|
#[command(arg_required_else_help = true)]
|
||||||
|
pub(super) struct CommandPack {
|
||||||
|
/// Add a package.
|
||||||
|
#[arg(short = 'a', long)]
|
||||||
|
pub(super) add: Option<String>,
|
||||||
|
/// Install all packages.
|
||||||
|
#[arg(short = 'i', long)]
|
||||||
|
pub(super) install: bool,
|
||||||
|
/// List all packages.
|
||||||
|
#[arg(short = 'l', long)]
|
||||||
|
pub(super) list: bool,
|
||||||
|
/// Upgrade all packages.
|
||||||
|
#[arg(short = 'u', long)]
|
||||||
|
pub(super) upgrade: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
|
|
@ -44,7 +86,7 @@ pub(super) struct CommandPub {
|
||||||
|
|
||||||
impl CommandPub {
|
impl CommandPub {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(super) fn receiver(&self) -> Result<u64> {
|
pub(super) fn receiver() -> Result<u64> {
|
||||||
if let Some(s) = std::env::var("YAZI_PID").ok().filter(|s| !s.is_empty()) {
|
if let Some(s) = std::env::var("YAZI_PID").ok().filter(|s| !s.is_empty()) {
|
||||||
Ok(s.parse()?)
|
Ok(s.parse()?)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -79,42 +121,39 @@ pub(super) struct CommandSub {
|
||||||
pub(super) kinds: String,
|
pub(super) kinds: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
|
||||||
#[command(arg_required_else_help = true)]
|
|
||||||
pub(super) struct CommandPack {
|
|
||||||
/// Add a package.
|
|
||||||
#[arg(short = 'a', long)]
|
|
||||||
pub(super) add: Option<String>,
|
|
||||||
/// Install all packages.
|
|
||||||
#[arg(short = 'i', long)]
|
|
||||||
pub(super) install: bool,
|
|
||||||
/// List all packages.
|
|
||||||
#[arg(short = 'l', long)]
|
|
||||||
pub(super) list: bool,
|
|
||||||
/// Upgrade all packages.
|
|
||||||
#[arg(short = 'u', long)]
|
|
||||||
pub(super) upgrade: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Macros
|
// --- Macros
|
||||||
macro_rules! impl_body {
|
macro_rules! impl_emit_body {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
impl $name {
|
impl $name {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(super) fn body(&self) -> Result<Cow<str>> {
|
pub(super) fn body(self) -> Result<String> {
|
||||||
if let Some(json) = &self.json {
|
Ok(serde_json::to_string(&(self.name, Cmd::parse_args(self.args.into_iter(), false)?))?)
|
||||||
Ok(json.into())
|
|
||||||
} else if let Some(str) = &self.str {
|
|
||||||
Ok(serde_json::to_string(str)?.into())
|
|
||||||
} else if !self.list.is_empty() {
|
|
||||||
Ok(serde_json::to_string(&self.list)?.into())
|
|
||||||
} else {
|
|
||||||
Ok("".into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_body!(CommandPub);
|
macro_rules! impl_pub_body {
|
||||||
impl_body!(CommandPubTo);
|
($name:ident) => {
|
||||||
|
impl $name {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn body(&self) -> Result<Cow<str>> {
|
||||||
|
Ok(if let Some(json) = &self.json {
|
||||||
|
json.into()
|
||||||
|
} else if let Some(str) = &self.str {
|
||||||
|
serde_json::to_string(str)?.into()
|
||||||
|
} else if !self.list.is_empty() {
|
||||||
|
serde_json::to_string(&self.list)?.into()
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_emit_body!(CommandEmit);
|
||||||
|
impl_emit_body!(CommandEmitTo);
|
||||||
|
|
||||||
|
impl_pub_body!(CommandPub);
|
||||||
|
impl_pub_body!(CommandPubTo);
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,47 @@ async fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match Args::parse().command {
|
match Args::parse().command {
|
||||||
|
Command::Emit(cmd) => {
|
||||||
|
yazi_boot::init_default();
|
||||||
|
yazi_dds::init();
|
||||||
|
if let Err(e) =
|
||||||
|
yazi_dds::Client::shot("dds-emit", CommandPub::receiver()?, &cmd.body()?).await
|
||||||
|
{
|
||||||
|
eprintln!("Cannot emit command: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::EmitTo(cmd) => {
|
||||||
|
yazi_boot::init_default();
|
||||||
|
yazi_dds::init();
|
||||||
|
if let Err(e) = yazi_dds::Client::shot("dds-emit", cmd.receiver, &cmd.body()?).await {
|
||||||
|
eprintln!("Cannot emit command: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::Pack(cmd) => {
|
||||||
|
package::init()?;
|
||||||
|
if cmd.install {
|
||||||
|
package::Package::install_from_config("plugin", false).await?;
|
||||||
|
package::Package::install_from_config("flavor", false).await?;
|
||||||
|
} else if cmd.list {
|
||||||
|
package::Package::list_from_config("plugin").await?;
|
||||||
|
package::Package::list_from_config("flavor").await?;
|
||||||
|
} else if cmd.upgrade {
|
||||||
|
package::Package::install_from_config("plugin", true).await?;
|
||||||
|
package::Package::install_from_config("flavor", true).await?;
|
||||||
|
} else if let Some(repo) = cmd.add {
|
||||||
|
package::Package::add_to_config(&repo).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Command::Pub(cmd) => {
|
Command::Pub(cmd) => {
|
||||||
yazi_boot::init_default();
|
yazi_boot::init_default();
|
||||||
yazi_dds::init();
|
yazi_dds::init();
|
||||||
if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver()?, &cmd.body()?).await {
|
if let Err(e) = yazi_dds::Client::shot(&cmd.kind, CommandPub::receiver()?, &cmd.body()?).await
|
||||||
|
{
|
||||||
eprintln!("Cannot send message: {e}");
|
eprintln!("Cannot send message: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -44,22 +81,6 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
tokio::signal::ctrl_c().await?;
|
tokio::signal::ctrl_c().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::Pack(cmd) => {
|
|
||||||
package::init()?;
|
|
||||||
if cmd.install {
|
|
||||||
package::Package::install_from_config("plugin", false).await?;
|
|
||||||
package::Package::install_from_config("flavor", false).await?;
|
|
||||||
} else if cmd.list {
|
|
||||||
package::Package::list_from_config("plugin").await?;
|
|
||||||
package::Package::list_from_config("flavor").await?;
|
|
||||||
} else if cmd.upgrade {
|
|
||||||
package::Package::install_from_config("plugin", true).await?;
|
|
||||||
package::Package::install_from_config("flavor", true).await?;
|
|
||||||
} else if let Some(repo) = cmd.add {
|
|
||||||
package::Package::add_to_config(&repo).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ keymap = [
|
||||||
|
|
||||||
# Toggle
|
# Toggle
|
||||||
{ on = "<Space>", run = [ "toggle", "arrow 1" ], desc = "Toggle the current selection state" },
|
{ on = "<Space>", run = [ "toggle", "arrow 1" ], desc = "Toggle the current selection state" },
|
||||||
{ on = "<C-a>", run = "toggle_all on", desc = "Select all files" },
|
{ on = "<C-a>", run = "toggle_all --state=on", desc = "Select all files" },
|
||||||
{ on = "<C-r>", run = "toggle_all", desc = "Invert selection of all files" },
|
{ on = "<C-r>", run = "toggle_all", desc = "Invert selection of all files" },
|
||||||
|
|
||||||
# Visual mode
|
# Visual mode
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
use yazi_macro::render_and;
|
use yazi_macro::render_and;
|
||||||
use yazi_proxy::AppProxy;
|
use yazi_proxy::AppProxy;
|
||||||
use yazi_shared::event::CmdCow;
|
use yazi_shared::{event::CmdCow, fs::Url};
|
||||||
|
|
||||||
use crate::tab::Tab;
|
use crate::tab::Tab;
|
||||||
|
|
||||||
struct Opt {
|
struct Opt {
|
||||||
|
url: Option<Url>,
|
||||||
state: Option<bool>,
|
state: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CmdCow> for Opt {
|
impl From<CmdCow> for Opt {
|
||||||
fn from(mut c: CmdCow) -> Self {
|
fn from(mut c: CmdCow) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: match c.take_first_str().as_deref() {
|
url: c.take_first_url(),
|
||||||
|
state: match c.str("state") {
|
||||||
Some("on") => Some(true),
|
Some("on") => Some(true),
|
||||||
Some("off") => Some(false),
|
Some("off") => Some(false),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -23,7 +25,7 @@ impl From<CmdCow> for Opt {
|
||||||
impl Tab {
|
impl Tab {
|
||||||
#[yazi_codegen::command]
|
#[yazi_codegen::command]
|
||||||
pub fn toggle(&mut self, opt: Opt) {
|
pub fn toggle(&mut self, opt: Opt) {
|
||||||
let Some(url) = self.current.hovered().map(|h| &h.url) else {
|
let Some(url) = opt.url.as_ref().or(self.current.hovered().map(|h| &h.url)) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ struct Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CmdCow> for Opt {
|
impl From<CmdCow> for Opt {
|
||||||
fn from(mut c: CmdCow) -> Self {
|
fn from(c: CmdCow) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: match c.take_first_str().as_deref() {
|
state: match c.str("state") {
|
||||||
Some("on") => Some(true),
|
Some("on") => Some(true),
|
||||||
Some("off") => Some(false),
|
Some("off") => Some(false),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,22 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
function M:setup()
|
function M:setup()
|
||||||
ps.sub_remote("dds-cd", function(url) ya.manager_emit("cd", { url }) end)
|
-- TODO: remove this
|
||||||
|
local b = false
|
||||||
|
ps.sub_remote("dds-cd", function(url)
|
||||||
|
if not b then
|
||||||
|
b = true
|
||||||
|
ya.notify {
|
||||||
|
title = "Deprecated DDS Event",
|
||||||
|
content = "The `dds-cd` event is deprecated, please use `ya emit cd /your/path` instead of `ya pub dds-cd --str /your/path`\n\nSee #1979 for details: https://github.com/sxyazi/yazi/pull/1979",
|
||||||
|
timeout = 20,
|
||||||
|
level = "warn",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
ya.manager_emit("cd", { url })
|
||||||
|
end)
|
||||||
|
|
||||||
|
ps.sub_remote("dds-emit", function(cmd) ya.manager_emit(cmd[1], cmd[2]) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ impl TryFrom<CmdCow> for PluginOpt {
|
||||||
|
|
||||||
let (args, _old_args) = if let Some(s) = c.str("args") {
|
let (args, _old_args) = if let Some(s) = c.str("args") {
|
||||||
(
|
(
|
||||||
Cmd::parse_args(shell_words::split(s)?.into_iter())?,
|
Cmd::parse_args(shell_words::split(s)?.into_iter(), true)?,
|
||||||
shell_words::split(s)?.into_iter().map(Data::String).collect(),
|
shell_words::split(s)?.into_iter().map(Data::String).collect(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -106,14 +106,17 @@ impl Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse
|
// Parse
|
||||||
pub fn parse_args(words: impl Iterator<Item = String>) -> Result<HashMap<DataKey, Data>> {
|
pub fn parse_args(
|
||||||
|
words: impl Iterator<Item = String>,
|
||||||
|
obase: bool,
|
||||||
|
) -> Result<HashMap<DataKey, Data>> {
|
||||||
let mut i = 0i64;
|
let mut i = 0i64;
|
||||||
words
|
words
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|word| {
|
.map(|word| {
|
||||||
let Some(arg) = word.strip_prefix("--") else {
|
let Some(arg) = word.strip_prefix("--") else {
|
||||||
i += 1;
|
i += 1;
|
||||||
return Ok((DataKey::Integer(i - 1), Data::String(word)));
|
return Ok((DataKey::Integer(i - obase as i64), Data::String(word)));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut parts = arg.splitn(2, '=');
|
let mut parts = arg.splitn(2, '=');
|
||||||
|
|
@ -167,7 +170,10 @@ impl FromStr for Cmd {
|
||||||
bail!("command name cannot be empty");
|
bail!("command name cannot be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Cmd { name: mem::take(&mut args[0]), args: Cmd::parse_args(args.into_iter().skip(1))? })
|
Ok(Cmd {
|
||||||
|
name: mem::take(&mut args[0]),
|
||||||
|
args: Cmd::parse_args(args.into_iter().skip(1), true)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{any::Any, borrow::Cow, collections::HashMap};
|
use std::{any::Any, borrow::Cow, collections::HashMap};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize, de};
|
||||||
|
|
||||||
use crate::{OrderedFloat, fs::Url};
|
use crate::{OrderedFloat, fs::Url};
|
||||||
|
|
||||||
|
|
@ -87,6 +87,7 @@ impl Data {
|
||||||
pub enum DataKey {
|
pub enum DataKey {
|
||||||
Nil,
|
Nil,
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
#[serde(deserialize_with = "Self::deserialize_integer")]
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Number(OrderedFloat),
|
Number(OrderedFloat),
|
||||||
String(Cow<'static, str>),
|
String(Cow<'static, str>),
|
||||||
|
|
@ -105,6 +106,32 @@ impl DataKey {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize_integer<'de, D>(deserializer: D) -> Result<i64, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct Visitor;
|
||||||
|
|
||||||
|
impl de::Visitor<'_> for Visitor {
|
||||||
|
type Value = i64;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("an integer or a string of an integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64<E>(self, value: i64) -> Result<i64, E> { Ok(value) }
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<i64, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
value.parse().map_err(de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(Visitor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<usize> for DataKey {
|
impl From<usize> for DataKey {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct OrderedFloat(f64);
|
pub struct OrderedFloat(f64);
|
||||||
|
|
||||||
|
|
@ -26,3 +26,17 @@ impl PartialEq for OrderedFloat {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for OrderedFloat {}
|
impl Eq for OrderedFloat {}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for OrderedFloat {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let f = f64::deserialize(deserializer)?;
|
||||||
|
if f.is_nan() {
|
||||||
|
Err(serde::de::Error::custom("NaN is not a valid OrderedFloat"))
|
||||||
|
} else {
|
||||||
|
Ok(Self::new(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue