This commit is contained in:
sxyazi 2024-04-17 19:34:55 +08:00
parent 8ed0def9c6
commit fd5cf1aad7
No known key found for this signature in database
15 changed files with 133 additions and 174 deletions

View file

@ -2,7 +2,7 @@ use std::{fmt, mem};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use serde::{de::{self, Visitor}, Deserializer}; use serde::{de::{self, Visitor}, Deserializer};
use yazi_shared::event::{Arg, Cmd}; use yazi_shared::event::{Cmd, Data};
pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error> pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
where where
@ -19,16 +19,16 @@ where
let mut cmd = Cmd { name: mem::take(&mut args[0]), ..Default::default() }; let mut cmd = Cmd { name: mem::take(&mut args[0]), ..Default::default() };
for (i, arg) in args.into_iter().skip(1).enumerate() { for (i, arg) in args.into_iter().skip(1).enumerate() {
if !arg.starts_with("--") { if !arg.starts_with("--") {
cmd.args.insert(i.to_string(), Arg::String(arg)); cmd.args.insert(i.to_string(), Data::String(arg));
continue; continue;
} }
let mut parts = arg.splitn(2, '='); let mut parts = arg.splitn(2, '=');
let key = parts.next().unwrap().trim_start_matches('-').to_owned(); let key = parts.next().unwrap().trim_start_matches('-').to_owned();
if let Some(val) = parts.next() { if let Some(val) = parts.next() {
cmd.args.insert(key, Arg::String(val.to_owned())); cmd.args.insert(key, Data::String(val.to_owned()));
} else { } else {
cmd.args.insert(key, Arg::Boolean(true)); cmd.args.insert(key, Data::Boolean(true));
} }
} }
Ok(cmd) Ok(cmd)

View file

@ -1,12 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use yazi_dds::ValueSendable; use yazi_shared::{event::{Cmd, Data}, fs::Url, render};
use yazi_shared::{event::Cmd, fs::Url, render};
use crate::{manager::{Manager, LINKED}, tasks::Tasks}; use crate::{manager::{Manager, LINKED}, tasks::Tasks};
pub struct Opt { pub struct Opt {
data: ValueSendable, data: Data,
} }
impl TryFrom<Cmd> for Opt { impl TryFrom<Cmd> for Opt {

View file

@ -1,15 +1,15 @@
use yazi_dds::ValueSendable; use yazi_shared::event::Data;
use super::Tasks; use super::Tasks;
impl Tasks { impl Tasks {
#[inline] #[inline]
pub fn plugin_micro(&self, name: String, args: Vec<ValueSendable>) { pub fn plugin_micro(&self, name: String, args: Vec<Data>) {
self.scheduler.plugin_micro(name, args); self.scheduler.plugin_micro(name, args);
} }
#[inline] #[inline]
pub fn plugin_macro(&self, name: String, args: Vec<ValueSendable>) { pub fn plugin_macro(&self, name: String, args: Vec<Data>) {
self.scheduler.plugin_macro(name, args); self.scheduler.plugin_macro(name, args);
} }
} }

View file

@ -1,24 +1,25 @@
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use serde::Serialize; use serde::Serialize;
use yazi_shared::event::Data;
use super::Body; use super::Body;
use crate::ValueSendable; use crate::Sendable;
#[derive(Debug)] #[derive(Debug)]
pub struct BodyCustom { pub struct BodyCustom {
pub kind: String, pub kind: String,
pub value: ValueSendable, pub data: Data,
} }
impl BodyCustom { impl BodyCustom {
#[inline] #[inline]
pub fn from_str(kind: &str, value: &str) -> anyhow::Result<Body<'static>> { pub fn from_str(kind: &str, data: &str) -> anyhow::Result<Body<'static>> {
Ok(Self { kind: kind.to_owned(), value: serde_json::from_str(value)? }.into()) Ok(Self { kind: kind.to_owned(), data: serde_json::from_str(data)? }.into())
} }
#[inline] #[inline]
pub fn from_lua(kind: &str, value: Value) -> mlua::Result<Body<'static>> { pub fn from_lua(kind: &str, data: Value) -> mlua::Result<Body<'static>> {
Ok(Self { kind: kind.to_owned(), value: value.try_into()? }.into()) Ok(Self { kind: kind.to_owned(), data: Sendable::value_to_data(data)? }.into())
} }
} }
@ -27,11 +28,11 @@ impl From<BodyCustom> for Body<'_> {
} }
impl IntoLua<'_> for BodyCustom { impl IntoLua<'_> for BodyCustom {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { self.value.into_lua(lua) } fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { Sendable::data_to_value(lua, self.data) }
} }
impl Serialize for BodyCustom { impl Serialize for BodyCustom {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serde::Serialize::serialize(&self.value, serializer) serde::Serialize::serialize(&self.data, serializer)
} }
} }

View file

@ -1,62 +1,26 @@
use std::collections::HashMap; use std::collections::HashMap;
use mlua::{ExternalError, IntoLua, Lua, Value, Variadic}; use mlua::{ExternalError, Lua, Table, Value, Variadic};
use serde::{Deserialize, Serialize}; use yazi_shared::{event::{Data, DataKey}, OrderedFloat};
use yazi_shared::OrderedFloat;
#[derive(Debug, Serialize, Deserialize)] pub struct Sendable;
#[serde(untagged)]
pub enum ValueSendable {
Nil,
Boolean(bool),
Integer(i64),
Number(f64),
String(String),
Table(HashMap<ValueSendableKey, ValueSendable>),
}
impl ValueSendable { impl Sendable {
pub fn try_from_variadic(values: Variadic<Value>) -> mlua::Result<Vec<Self>> { pub fn value_to_data(value: Value) -> mlua::Result<Data> {
let mut vec = Vec::with_capacity(values.len());
for value in values {
vec.push(Self::try_from(value)?);
}
Ok(vec)
}
pub fn into_table_string(self) -> HashMap<String, String> {
let Self::Table(table) = self else {
return Default::default();
};
let mut map = HashMap::with_capacity(table.len());
for pair in table {
if let (ValueSendableKey::String(k), Self::String(v)) = pair {
map.insert(k, v);
}
}
map
}
}
impl<'a> TryFrom<Value<'a>> for ValueSendable {
type Error = mlua::Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
Ok(match value { Ok(match value {
Value::Nil => Self::Nil, Value::Nil => Data::Nil,
Value::Boolean(b) => Self::Boolean(b), Value::Boolean(b) => Data::Boolean(b),
Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?, Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?,
Value::Integer(n) => Self::Integer(n), Value::Integer(n) => Data::Integer(n),
Value::Number(n) => Self::Number(n), Value::Number(n) => Data::Number(n),
Value::String(s) => Self::String(s.to_str()?.to_owned()), Value::String(s) => Data::String(s.to_str()?.to_owned()),
Value::Table(t) => { Value::Table(t) => {
let mut map = HashMap::with_capacity(t.len().map(|l| l as usize)?); let mut map = HashMap::with_capacity(t.len().map(|l| l as usize)?);
for result in t.pairs::<Value, Value>() { for result in t.pairs::<Value, Value>() {
let (k, v) = result?; let (k, v) = result?;
map.insert(Self::try_from(k)?.try_into()?, v.try_into()?); map.insert(Self::value_to_key(k)?, Self::value_to_data(v)?);
} }
Self::Table(map) Data::Table(map)
} }
Value::Function(_) => Err("function is not supported".into_lua_err())?, Value::Function(_) => Err("function is not supported".into_lua_err())?,
Value::Thread(_) => Err("thread is not supported".into_lua_err())?, Value::Thread(_) => Err("thread is not supported".into_lua_err())?,
@ -64,66 +28,72 @@ impl<'a> TryFrom<Value<'a>> for ValueSendable {
Value::Error(_) => Err("error is not supported".into_lua_err())?, Value::Error(_) => Err("error is not supported".into_lua_err())?,
}) })
} }
}
impl IntoLua<'_> for ValueSendable { pub fn value_to_key(value: Value) -> mlua::Result<DataKey> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { Ok(match value {
match self { Value::Nil => DataKey::Nil,
Self::Nil => Ok(Value::Nil), Value::Boolean(v) => DataKey::Boolean(v),
Self::Boolean(v) => Ok(Value::Boolean(v)), Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?,
Self::Integer(v) => Ok(Value::Integer(v)), Value::Integer(v) => DataKey::Integer(v),
Self::Number(v) => Ok(Value::Number(v)), Value::Number(v) => DataKey::Number(OrderedFloat::new(v)),
Self::String(v) => Ok(Value::String(lua.create_string(v)?)), Value::String(v) => DataKey::String(v.to_str()?.to_owned()),
Self::Table(v) => { Value::Table(_) => Err("table is not supported".into_lua_err())?,
Value::Function(_) => Err("function is not supported".into_lua_err())?,
Value::Thread(_) => Err("thread is not supported".into_lua_err())?,
Value::UserData(_) => Err("userdata is not supported".into_lua_err())?,
Value::Error(_) => Err("error is not supported".into_lua_err())?,
})
}
pub fn data_to_value(lua: &Lua, data: Data) -> mlua::Result<Value> {
match data {
Data::Nil => Ok(Value::Nil),
Data::Boolean(v) => Ok(Value::Boolean(v)),
Data::Integer(v) => Ok(Value::Integer(v)),
Data::Number(v) => Ok(Value::Number(v)),
Data::String(v) => Ok(Value::String(lua.create_string(v)?)),
Data::Table(v) => {
let seq_len = v.keys().filter(|&k| !k.is_numeric()).count(); let seq_len = v.keys().filter(|&k| !k.is_numeric()).count();
let table = lua.create_table_with_capacity(seq_len, v.len() - seq_len)?; let table = lua.create_table_with_capacity(seq_len, v.len() - seq_len)?;
for (k, v) in v { for (k, v) in v {
table.raw_set(k, v)?; table.raw_set(Self::key_to_value(lua, k)?, Self::data_to_value(lua, v)?)?;
} }
Ok(Value::Table(table)) Ok(Value::Table(table))
} }
} }
} }
}
#[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result<Value> {
#[serde(untagged)] match key {
pub enum ValueSendableKey { DataKey::Nil => Ok(Value::Nil),
Nil, DataKey::Boolean(k) => Ok(Value::Boolean(k)),
Boolean(bool), DataKey::Integer(k) => Ok(Value::Integer(k)),
Integer(i64), DataKey::Number(k) => Ok(Value::Number(k.get())),
Number(OrderedFloat), DataKey::String(k) => Ok(Value::String(lua.create_string(k)?)),
String(String),
}
impl ValueSendableKey {
#[inline]
fn is_numeric(&self) -> bool { matches!(self, Self::Integer(_) | Self::Number(_)) }
}
impl TryInto<ValueSendableKey> for ValueSendable {
type Error = mlua::Error;
fn try_into(self) -> Result<ValueSendableKey, Self::Error> {
Ok(match self {
Self::Nil => ValueSendableKey::Nil,
Self::Boolean(v) => ValueSendableKey::Boolean(v),
Self::Integer(v) => ValueSendableKey::Integer(v),
Self::Number(v) => ValueSendableKey::Number(OrderedFloat::new(v)),
Self::String(v) => ValueSendableKey::String(v),
Self::Table(_) => Err("table is not supported".into_lua_err())?,
})
}
}
impl IntoLua<'_> for ValueSendableKey {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
match self {
Self::Nil => Ok(Value::Nil),
Self::Boolean(k) => Ok(Value::Boolean(k)),
Self::Integer(k) => Ok(Value::Integer(k)),
Self::Number(k) => Ok(Value::Number(k.get())),
Self::String(k) => Ok(Value::String(lua.create_string(k)?)),
} }
} }
pub fn vec_to_table(lua: &Lua, data: Vec<Data>) -> mlua::Result<Table> {
let mut vec = Vec::with_capacity(data.len());
for v in data.into_iter() {
vec.push(Self::data_to_value(lua, v)?);
}
lua.create_sequence_from(vec)
}
pub fn vec_to_variadic(lua: &Lua, data: Vec<Data>) -> mlua::Result<Variadic<Value>> {
let mut vec = Vec::with_capacity(data.len());
for v in data {
vec.push(Self::data_to_value(lua, v)?);
}
Ok(Variadic::from_iter(vec))
}
pub fn variadic_to_vec(values: Variadic<Value>) -> mlua::Result<Vec<Data>> {
let mut vec = Vec::with_capacity(values.len());
for value in values {
vec.push(Self::value_to_data(value)?);
}
Ok(vec)
}
} }

View file

@ -3,6 +3,7 @@ use std::fmt::Display;
use mlua::TableExt; use mlua::TableExt;
use scopeguard::defer; use scopeguard::defer;
use tracing::warn; use tracing::warn;
use yazi_dds::Sendable;
use yazi_plugin::{loader::LOADER, OptData, RtRef, LUA}; use yazi_plugin::{loader::LOADER, OptData, RtRef, LUA};
use yazi_shared::{emit, event::Cmd, Layer}; use yazi_shared::{emit, event::Cmd, Layer};
@ -56,7 +57,7 @@ impl App {
if let Some(cb) = opt.data.cb { if let Some(cb) = opt.data.cb {
cb(&LUA, plugin) cb(&LUA, plugin)
} else { } else {
plugin.call_method("entry", opt.data.args) plugin.call_method("entry", Sendable::vec_to_table(&LUA, opt.data.args)?)
} }
}); });
} }

View file

@ -1,11 +1,12 @@
use mlua::{ExternalError, ExternalResult, Table, TableExt}; use mlua::{ExternalError, ExternalResult, Table, TableExt};
use tokio::runtime::Handle; use tokio::runtime::Handle;
use yazi_dds::ValueSendable; use yazi_dds::Sendable;
use yazi_shared::event::Data;
use super::slim_lua; use super::slim_lua;
use crate::loader::LOADER; use crate::loader::LOADER;
pub async fn entry(name: String, args: Vec<ValueSendable>) -> mlua::Result<()> { pub async fn entry(name: String, args: Vec<Data>) -> mlua::Result<()> {
LOADER.ensure(&name).await.into_lua_err()?; LOADER.ensure(&name).await.into_lua_err()?;
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
@ -16,7 +17,8 @@ pub async fn entry(name: String, args: Vec<ValueSendable>) -> mlua::Result<()> {
return Err("unloaded plugin".into_lua_err()); return Err("unloaded plugin".into_lua_err());
}; };
Handle::current().block_on(plugin.call_async_method("entry", args)) Handle::current()
.block_on(plugin.call_async_method("entry", Sendable::vec_to_table(&lua, args)))
}) })
.await .await
.into_lua_err()? .into_lua_err()?

View file

@ -1,7 +1,6 @@
use anyhow::bail; use anyhow::bail;
use mlua::{Lua, Table}; use mlua::{Lua, Table};
use yazi_dds::ValueSendable; use yazi_shared::event::{Cmd, Data};
use yazi_shared::event::Cmd;
pub struct Opt { pub struct Opt {
pub name: String, pub name: String,
@ -11,7 +10,7 @@ pub struct Opt {
#[derive(Default)] #[derive(Default)]
pub struct OptData { pub struct OptData {
pub args: Vec<ValueSendable>, pub args: Vec<Data>,
pub cb: Option<Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send>>, pub cb: Option<Box<dyn FnOnce(&Lua, Table) -> mlua::Result<()> + Send>>,
} }
@ -26,7 +25,7 @@ impl TryFrom<Cmd> for Opt {
let mut data: OptData = c.take_data().unwrap_or_default(); let mut data: OptData = c.take_data().unwrap_or_default();
if let Some(args) = c.get_str("args") { if let Some(args) = c.get_str("args") {
data.args = shell_words::split(args)?.into_iter().map(ValueSendable::String).collect(); data.args = shell_words::split(args)?.into_iter().map(Data::String).collect();
} }
Ok(Self { name, sync: c.get_bool("sync"), data }) Ok(Self { name, sync: c.get_bool("sync"), data })

View file

@ -1,7 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use mlua::{ExternalError, Lua, Table, Value}; use mlua::{ExternalError, Lua, Table, Value};
use yazi_dds::ValueSendable;
use yazi_shared::{emit, event::Cmd, render, Layer}; use yazi_shared::{emit, event::Cmd, render, Layer};
use super::Utils; use super::Utils;

View file

@ -1,7 +1,7 @@
use mlua::{ExternalError, ExternalResult, Function, IntoLua, Lua, Table, Value, Variadic}; use mlua::{ExternalError, ExternalResult, Function, IntoLua, Lua, Table, Value, Variadic};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use yazi_dds::ValueSendable; use yazi_dds::Sendable;
use yazi_shared::{emit, event::Cmd, Layer}; use yazi_shared::{emit, event::{Cmd, Data}, Layer};
use super::Utils; use super::Utils;
use crate::{loader::LOADER, runtime::RtRef, OptData}; use crate::{loader::LOADER, runtime::RtRef, OptData};
@ -37,7 +37,7 @@ impl Utils {
return Err("`ya.sync()` must be called in a plugin").into_lua_err(); return Err("`ya.sync()` must be called in a plugin").into_lua_err();
}; };
Self::retrieve(cur, block, args).await Sendable::vec_to_variadic(lua, Self::retrieve(cur, block, args).await?)
}) })
})?, })?,
)?; )?;
@ -49,9 +49,9 @@ impl Utils {
name: String, name: String,
calls: usize, calls: usize,
args: Variadic<Value<'_>>, args: Variadic<Value<'_>>,
) -> mlua::Result<mlua::Variadic<ValueSendable>> { ) -> mlua::Result<Vec<Data>> {
let args = ValueSendable::try_from_variadic(args)?; let args = Sendable::variadic_to_vec(args)?;
let (tx, rx) = oneshot::channel::<Vec<ValueSendable>>(); let (tx, rx) = oneshot::channel::<Vec<Data>>();
let data = OptData { let data = OptData {
cb: Some({ cb: Some({
@ -64,11 +64,10 @@ impl Utils {
let mut self_args = Vec::with_capacity(args.len() + 1); let mut self_args = Vec::with_capacity(args.len() + 1);
self_args.push(Value::Table(plugin)); self_args.push(Value::Table(plugin));
for arg in args { for arg in args {
self_args.push(arg.into_lua(lua)?); self_args.push(Sendable::data_to_value(lua, arg)?);
} }
let values = let values = Sendable::variadic_to_vec(block.call(Variadic::from_iter(self_args))?)?;
ValueSendable::try_from_variadic(block.call(Variadic::from_iter(self_args))?)?;
tx.send(values).map_err(|_| "send failed".into_lua_err()) tx.send(values).map_err(|_| "send failed".into_lua_err())
}) })
}), }),
@ -80,8 +79,8 @@ impl Utils {
Layer::App Layer::App
)); ));
Ok(Variadic::from_iter(rx.await.map_err(|_| { rx.await.map_err(|_| {
format!("Failed to execute sync block-{calls} in `{name}` plugin").into_lua_err() format!("Failed to execute sync block-{calls} in `{name}` plugin").into_lua_err()
})?)) })
} }
} }

View file

@ -1,4 +1,4 @@
use yazi_dds::ValueSendable; use yazi_shared::event::Data;
#[derive(Debug)] #[derive(Debug)]
pub enum PluginOp { pub enum PluginOp {
@ -17,5 +17,5 @@ impl PluginOp {
pub struct PluginOpEntry { pub struct PluginOpEntry {
pub id: usize, pub id: usize,
pub name: String, pub name: String,
pub args: Vec<ValueSendable>, pub args: Vec<Data>,
} }

View file

@ -4,8 +4,8 @@ use futures::{future::BoxFuture, FutureExt};
use parking_lot::Mutex; use parking_lot::Mutex;
use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle}; use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle};
use yazi_config::{open::Opener, plugin::PluginRule, TASKS}; use yazi_config::{open::Opener, plugin::PluginRule, TASKS};
use yazi_dds::{Pump, ValueSendable}; use yazi_dds::Pump;
use yazi_shared::{fs::{unique_path, Url}, Throttle}; use yazi_shared::{event::Data, fs::{unique_path, Url}, Throttle};
use super::{Ongoing, TaskProg, TaskStage}; use super::{Ongoing, TaskProg, TaskStage};
use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Preload, PreloadOpRule, PreloadOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Preload, PreloadOpRule, PreloadOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL};
@ -182,7 +182,7 @@ impl Scheduler {
); );
} }
pub fn plugin_micro(&self, name: String, args: Vec<ValueSendable>) { pub fn plugin_micro(&self, name: String, args: Vec<Data>) {
let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{name}`")); let id = self.ongoing.lock().add(TaskKind::User, format!("Run micro plugin `{name}`"));
let plugin = self.plugin.clone(); let plugin = self.plugin.clone();
@ -195,7 +195,7 @@ impl Scheduler {
); );
} }
pub fn plugin_macro(&self, name: String, args: Vec<ValueSendable>) { pub fn plugin_macro(&self, name: String, args: Vec<Data>) {
let id = self.ongoing.lock().add(TaskKind::User, format!("Run macro plugin `{name}`")); let id = self.ongoing.lock().add(TaskKind::User, format!("Run macro plugin `{name}`"));
self.plugin.macro_(PluginOpEntry { id, name, args }).ok(); self.plugin.macro_(PluginOpEntry { id, name, args }).ok();

View file

@ -1,11 +1,11 @@
use std::{any::Any, collections::HashMap, fmt::{self, Display}}; use std::{any::Any, collections::HashMap, fmt::{self, Display}};
use super::Arg; use super::Data;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Cmd { pub struct Cmd {
pub name: String, pub name: String,
pub args: HashMap<String, Arg>, pub args: HashMap<String, Data>,
pub data: Option<Box<dyn Any + Send>>, pub data: Option<Box<dyn Any + Send>>,
} }
@ -17,20 +17,20 @@ impl Cmd {
pub fn args(name: &str, args: Vec<String>) -> Self { pub fn args(name: &str, args: Vec<String>) -> Self {
Self { Self {
name: name.to_owned(), name: name.to_owned(),
args: args.into_iter().enumerate().map(|(i, s)| (i.to_string(), Arg::String(s))).collect(), args: args.into_iter().enumerate().map(|(i, s)| (i.to_string(), Data::String(s))).collect(),
..Default::default() ..Default::default()
} }
} }
#[inline] #[inline]
pub fn with(mut self, name: impl ToString, value: impl ToString) -> Self { pub fn with(mut self, name: impl ToString, value: impl ToString) -> Self {
self.args.insert(name.to_string(), Arg::String(value.to_string())); self.args.insert(name.to_string(), Data::String(value.to_string()));
self self
} }
#[inline] #[inline]
pub fn with_bool(mut self, name: impl ToString, state: bool) -> Self { pub fn with_bool(mut self, name: impl ToString, state: bool) -> Self {
self.args.insert(name.to_string(), Arg::Boolean(state)); self.args.insert(name.to_string(), Data::Boolean(state));
self self
} }
@ -41,11 +41,11 @@ impl Cmd {
} }
#[inline] #[inline]
pub fn get_str(&self, name: &str) -> Option<&str> { self.args.get(name).and_then(Arg::as_str) } pub fn get_str(&self, name: &str) -> Option<&str> { self.args.get(name).and_then(Data::as_str) }
#[inline] #[inline]
pub fn get_bool(&self, name: &str) -> bool { pub fn get_bool(&self, name: &str) -> bool {
self.args.get(name).and_then(Arg::as_bool).unwrap_or(false) self.args.get(name).and_then(Data::as_bool).unwrap_or(false)
} }
#[inline] #[inline]
@ -55,19 +55,19 @@ impl Cmd {
#[inline] #[inline]
pub fn take_first_str(&mut self) -> Option<String> { pub fn take_first_str(&mut self) -> Option<String> {
if let Some(Arg::String(s)) = self.args.remove("0") { Some(s) } else { None } if let Some(Data::String(s)) = self.args.remove("0") { Some(s) } else { None }
} }
#[inline] #[inline]
pub fn take_name_str(&mut self, name: &str) -> Option<String> { pub fn take_name_str(&mut self, name: &str) -> Option<String> {
if let Some(Arg::String(s)) = self.args.remove(name) { Some(s) } else { None } if let Some(Data::String(s)) = self.args.remove(name) { Some(s) } else { None }
} }
pub fn shallow_clone(&self) -> Self { pub fn shallow_clone(&self) -> Self {
let args = self let args = self
.args .args
.iter() .iter()
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), Arg::String(s.to_owned())))) .filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), Data::String(s.to_owned()))))
.collect(); .collect();
Self { name: self.name.clone(), args, data: None } Self { name: self.name.clone(), args, data: None }

View file

@ -1,6 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::bail;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::OrderedFloat; use crate::OrderedFloat;
@ -8,16 +7,16 @@ use crate::OrderedFloat;
// --- Arg // --- Arg
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum Arg { pub enum Data {
Nil, Nil,
Boolean(bool), Boolean(bool),
Integer(i64), Integer(i64),
Number(f64), Number(f64),
String(String), String(String),
Table(HashMap<ArgKey, Arg>), Table(HashMap<DataKey, Data>),
} }
impl Arg { impl Data {
#[inline] #[inline]
pub fn as_bool(&self) -> Option<bool> { pub fn as_bool(&self) -> Option<bool> {
match self { match self {
@ -41,7 +40,7 @@ impl Arg {
let mut map = HashMap::with_capacity(table.len()); let mut map = HashMap::with_capacity(table.len());
for pair in table { for pair in table {
if let (ArgKey::String(k), Self::String(v)) = pair { if let (DataKey::String(k), Self::String(v)) = pair {
map.insert(k, v); map.insert(k, v);
} }
} }
@ -52,7 +51,7 @@ impl Arg {
// --- Key // --- Key
#[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum ArgKey { pub enum DataKey {
Nil, Nil,
Boolean(bool), Boolean(bool),
Integer(i64), Integer(i64),
@ -60,17 +59,7 @@ pub enum ArgKey {
String(String), String(String),
} }
impl TryInto<ArgKey> for Arg { impl DataKey {
type Error = anyhow::Error; #[inline]
pub fn is_numeric(&self) -> bool { matches!(self, Self::Integer(_) | Self::Number(_)) }
fn try_into(self) -> Result<ArgKey, Self::Error> {
Ok(match self {
Self::Nil => ArgKey::Nil,
Self::Boolean(v) => ArgKey::Boolean(v),
Self::Integer(v) => ArgKey::Integer(v),
Self::Number(v) => ArgKey::Number(OrderedFloat::new(v)),
Self::String(v) => ArgKey::String(v),
Self::Table(_) => bail!("table is not supported"),
})
}
} }

View file

@ -1,11 +1,11 @@
#![allow(clippy::module_inception)] #![allow(clippy::module_inception)]
mod arg;
mod cmd; mod cmd;
mod data;
mod event; mod event;
mod render; mod render;
pub use arg::*;
pub use cmd::*; pub use cmd::*;
pub use data::*;
pub use event::*; pub use event::*;
pub use render::*; pub use render::*;