yazi/yazi-shared/src/event/data.rs

239 lines
5 KiB
Rust

use std::{any::Any, borrow::Cow, collections::HashMap};
use serde::{Deserialize, Serialize, de};
use crate::{Id, OrderedFloat, url::{Url, UrnBuf}};
// --- Data
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Data {
Nil,
Boolean(bool),
Integer(i64),
Number(f64),
String(Cow<'static, str>),
List(Vec<Data>),
Dict(HashMap<DataKey, Data>),
Id(Id),
#[serde(skip_deserializing)]
Url(Url),
#[serde(skip_deserializing)]
Urn(UrnBuf),
#[serde(skip)]
Bytes(Vec<u8>),
#[serde(skip)]
Any(Box<dyn Any + Send + Sync>),
}
impl Data {
#[inline]
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Boolean(b) => Some(*b),
Self::String(s) if s == "no" => Some(false),
Self::String(s) if s == "yes" => Some(true),
_ => None,
}
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s),
_ => None,
}
}
#[inline]
pub fn into_any<T: 'static>(self) -> Option<T> {
match self {
Self::Any(b) => b.downcast::<T>().ok().map(|b| *b),
_ => None,
}
}
#[inline]
pub fn into_url(self) -> Option<Url> {
match self {
Data::String(s) => Url::try_from(s.as_ref()).ok(),
Data::Url(u) => Some(u),
_ => None,
}
}
pub fn into_dict_string(self) -> HashMap<Cow<'static, str>, Cow<'static, str>> {
let Self::Dict(dict) = self else {
return Default::default();
};
let mut map = HashMap::with_capacity(dict.len());
for pair in dict {
if let (DataKey::String(k), Self::String(v)) = pair {
map.insert(k, v);
}
}
map
}
#[inline]
pub fn to_url(&self) -> Option<Url> {
match self {
Self::String(s) => Url::try_from(s.as_ref()).ok(),
Self::Url(u) => Some(u.clone()),
_ => None,
}
}
}
impl From<bool> for Data {
fn from(value: bool) -> Self { Self::Boolean(value) }
}
impl From<i32> for Data {
fn from(value: i32) -> Self { Self::Integer(value as i64) }
}
impl From<i64> for Data {
fn from(value: i64) -> Self { Self::Integer(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(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)]
pub enum DataKey {
Nil,
Boolean(bool),
#[serde(deserialize_with = "Self::deserialize_integer")]
Integer(i64),
Number(OrderedFloat),
String(Cow<'static, str>),
Id(Id),
#[serde(skip_deserializing)]
Url(Url),
#[serde(skip_deserializing)]
Urn(UrnBuf),
#[serde(skip)]
Bytes(Vec<u8>),
}
impl DataKey {
#[inline]
pub fn is_integer(&self) -> bool { matches!(self, Self::Integer(_)) }
#[inline]
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s),
_ => 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 {
fn from(value: usize) -> Self { Self::Integer(value as i64) }
}
impl From<&'static str> for DataKey {
fn from(value: &'static str) -> Self { Self::String(Cow::Borrowed(value)) }
}
impl From<String> for DataKey {
fn from(value: String) -> Self { Self::String(Cow::Owned(value)) }
}
// --- Macros
macro_rules! impl_as_integer {
($t:ty, $name:ident) => {
impl Data {
#[inline]
pub fn $name(&self) -> Option<$t> {
match self {
Data::Integer(i) => <$t>::try_from(*i).ok(),
Data::String(s) => s.parse().ok(),
Data::Id(i) => <$t>::try_from(i.get()).ok(),
_ => None,
}
}
}
};
}
macro_rules! impl_as_number {
($t:ty, $name:ident) => {
impl Data {
#[inline]
pub fn $name(&self) -> Option<$t> {
match self {
Data::Integer(i) if *i == (*i as $t as _) => Some(*i as $t),
Data::Number(n) => <$t>::try_from(*n).ok(),
Data::String(s) => s.parse().ok(),
Data::Id(i) if i.0 == (i.0 as $t as _) => Some(i.0 as $t),
_ => None,
}
}
}
};
}
impl_as_integer!(usize, as_usize);
impl_as_integer!(isize, as_isize);
impl_as_integer!(i16, as_i16);
impl_as_integer!(i32, as_i32);
impl_as_integer!(crate::Id, as_id);
impl_as_number!(f64, as_f64);