use std::
{
borrow::Cow,
collections::HashMap,
fmt::{self, Display, Formatter},
ops::{Deref, DerefMut}
};
pub trait Component: Display + std::fmt::Debug + std::any::Any
{
fn clone(&self) -> Box<dyn Component>;
fn eq(&self, other: &'static dyn Component) -> bool;
fn as_map(&self) -> HashMap<Cow<'static, str>, Cow<'static, str>>;
}
impl serde::Serialize for Box<dyn Component>
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer
{
self.as_map().serialize(serializer)
}
}
impl Clone for Box<dyn Component>
{
fn clone(&self) -> Self
{
Component::clone(&**self)
}
}
#[derive(Default, Debug)]
#[repr(transparent)]
pub struct ComponentVec(pub Vec<(Cow<'static, str>, Box<dyn Component>)>);
impl Deref for ComponentVec
{
type Target = Vec<(Cow<'static, str>, Box<dyn Component>)>;
fn deref(&self) -> &Self::Target
{
&self.0
}
}
impl DerefMut for ComponentVec
{
fn deref_mut(&mut self) -> &mut Self::Target
{
&mut self.0
}
}
impl PartialEq for ComponentVec
{
fn eq(&self, other: &Self) -> bool
{
self.0.len() == other.0.len() &&
matches!(self.0.iter().enumerate().filter(|(i, (_name, cmp))|
cmp.eq(&*unsafe{std::mem::transmute::<&ComponentVec, &'static ComponentVec>(other)}
.0[*i].1)).next(), None)
}
}
impl Clone for ComponentVec
{
fn clone(&self) -> Self
{
Self(self.0.iter().map(|(name, cmp)| (name.clone(), (*cmp).clone())).collect())
}
}
#[macro_export]
macro_rules! component_struct
{
($(#[$outer:meta])* $name:ident $(, $field:ident: $field_name:literal $ty:ty = $default:expr)*) =>
{
component_struct!($(#[$outer])* $name concat!($($field_name, ": {};"),*) $(, $field: $field_name $ty = $default)*);
};
($(#[$outer:meta])* $name:ident $(:$alt:ident)? $fmt:expr $(, $field:ident: $field_name:literal $ty:ty = $default:expr)*) =>
{
$(#[$outer])*
#[derive(Debug, Clone, PartialEq, serde::Serialize)]
pub struct $name
{
$(
pub $field: $ty
),*
}
impl std::fmt::Display for $name
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
{
if stringify!($($alt)?).len() < 2
{
$(
if self.$field != Self::DEFAULT.$field
{
if $field_name.len() <= 1
{
write!(f, "{};", self.$field)?;
}
else
{
write!(f, concat!($field_name, ": {};"), self.$field)?;
}
}
)*
Ok(())
}
else
{
write!(f, $fmt, $(self.$field),*)
}
}
}
impl ConstDefault for $name
{
const DEFAULT: Self = Self
{
$($field: $default),*
};
}
impl Component for $name
{
fn clone(&self) -> Box<dyn Component>
{
Box::new(Clone::clone(self))
}
fn eq(&self, other: &'static dyn Component) -> bool
{
match (&&*other as &dyn std::any::Any).downcast_ref::<&&$name>()
{
Some(other) => self == **other,
None => false
}
}
fn as_map(&self) -> std::collections::HashMap<Cow<'static, str>, Cow<'static, str>>
{
#[allow(unused_mut)]
let mut map = std::collections::HashMap::new();
$( if $field_name.len() < 1
{
let mut inner_map = std::collections::HashMap::new();
for (k, v) in self.$field
.to_string()
.split(";")
.map(str::trim)
.filter_map(|s| s.split_once(":"))
{
inner_map.insert
(
k.trim().to_owned().into(),
v.trim().to_owned().into()
);
}
map.extend(inner_map);
}
else
{
map.insert($field_name.into(), self.$field.to_string().into());
})*
map
}
}
}
}
#[macro_export]
macro_rules! component
{
($($cmp:ident)::* $(, $field:ident: $val:expr)*) =>
{
$($cmp)::*
{
$($field: $val,)*
..$($cmp)::*::DEFAULT
}
}
}
#[macro_export]
macro_rules! simple_enum
{
($(#[$outer:meta])* $name:ident $(, $variant:ident => $s:literal)*) =>
{
$(#[$outer])*
#[derive(Clone, Copy, PartialEq, Debug, serde::Serialize)]
pub enum $name {$($variant),* }
impl std::fmt::Display for $name
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
{
write!(f, "{}", match self { $(Self::$variant => $s),* })
}
}
}
}
#[macro_export]
macro_rules! complex_enum
{
($(#[$outer:meta])* $name:ident $(, $variant:ident $fmt:expr => { $($field:ident: $ty:ty),* })*) =>
{
$(#[$outer])*
#[derive(Debug, Clone, PartialEq, serde::Serialize)]
pub enum $name
{
$($variant { $($field: $ty),* }),*
}
impl std::fmt::Display for $name
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
{
match self
{
$(Self::$variant { $($field),* } => write!(f, $fmt, $($field),*)),*
}
}
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, serde::Serialize)]
pub struct List<T: Display + ToOwned + std::fmt::Debug + Clone + PartialEq + serde::Serialize + 'static>
(pub Cow<'static, [T]>)
where [T]: ToOwned, <[T] as ToOwned>::Owned: std::fmt::Debug;
impl<T: Display + ToOwned + 'static + std::fmt::Debug + Clone + PartialEq + serde::Serialize> Display for List<T>
where [T]: ToOwned, <[T] as ToOwned>::Owned: std::fmt::Debug
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result
{
let len = self.0.len();
for (i, item) in self.0.iter().enumerate()
{
if i < len - 1
{
write!(f, "{},", item)?;
}
else
{
std::fmt::Display::fmt(&item, f)?;
}
}
Ok(())
}
}
impl<T: Display + ToOwned + std::fmt::Debug + 'static + Clone + PartialEq + serde::Serialize> List<T>
where [T]: ToOwned, <[T] as ToOwned>::Owned: std::fmt::Debug
{
pub const DEFAULT: List<T> = List(Cow::Borrowed(&[]));
}