use super::{data::Number, Value};
use paste::paste;
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Transform(Vec<TransformFunction>);
macro_rules! transform {
($($ident:ident($($arg:ident),+);)+) => {
paste! {
#[derive(Clone, Copy, Debug, PartialEq)]
enum TransformFunction {
$([<$ident:camel>] { $($arg: Number),+ }),+
}
impl Transform {
$(
pub fn [<$ident:lower>]<$([<$arg:upper>]),+>(
mut self, $($arg: [<$arg:upper>]),+
) -> Self
where
$([<$arg:upper>]: Into<Number>),+
{
self.0.push(TransformFunction::[<$ident:camel>] {
$($arg: $arg.into()),+
});
self
}
pub fn [<new_ $ident:lower>]<$([<$arg:upper>]),+>(
$($arg: [<$arg:upper>]),+
) -> Self
where
$([<$arg:upper>]: Into<Number>),+
{
Self(vec![TransformFunction::[<$ident:camel>] {
$($arg: $arg.into()),+
}])
}
)+
}
impl Display for TransformFunction {
#[allow(unused_assignments)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
$(
Self::[<$ident:camel>] { $($arg),+ } => {
write!(f, concat!(stringify!($ident), "("))?;
let mut first = true;
$(
if !first {
write!(f, ",")?;
}
first = false;
write!(f, "{}", $arg.value_to_string(false))?;
)+
write!(f, ")")?;
}
),+
};
Ok(())
}
}
}
};
}
transform! {
translate(x, y);
scale(x, y);
skewX(x);
skewY(y);
rotate(deg);
}
impl Transform {
pub const fn new() -> Self {
Self(Vec::new())
}
}
pub(crate) struct DisplayTransform<'a>(&'a [TransformFunction], bool);
impl Display for DisplayTransform<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (i, tf) in self.0.iter().enumerate() {
if self.1 && i > 0 {
write!(f, " ")?;
}
write!(f, "{tf}")?;
}
Ok(())
}
}
impl Value for Transform {
fn value_to_string(&self, pretty: bool) -> String {
DisplayTransform(&self.0, pretty).to_string()
}
}