use crate::calc::Calc;
use paste::paste;
use std::{fmt, ops};
pub trait Unit {
fn zero() -> Self;
fn half() -> Self;
fn full() -> Self;
}
macro_rules! units {
(@one $name: ident: $css: literal) => {
#[derive(Clone, Debug, Copy, PartialEq, PartialOrd, From)]
pub struct $name(f32);
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:.2}", self.0)?;
write!(f, $css)
}
}
};
(@two $name: ident: $css: literal -> $fn_ty: ty) => {
paste! {
pub fn [<$name:snake>](value: impl Into<$name>) -> $fn_ty {
(value.into()).into()
}
}
};
(@three $name: ident: $css: literal -> $target: ty) => {
impl ops::Add<Length> for $name {
type Output = Calc;
fn add(self, rhs: Length) -> Self::Output {
Calc::from(<$target>::from(self)).add(rhs)
}
}
impl ops::Add<$name> for $name {
type Output = Calc;
fn add(self, rhs: $name) -> Self::Output {
Calc::from(<$target>::from(self)).add(<$target>::from(rhs))
}
}
impl<T> ops::Add<Option<T>> for $name
where
$name: ops::Add<T, Output = Calc>,
{
type Output = Calc;
fn add(self, rhs: Option<T>) -> Self::Output {
if let Some(length) = rhs {
self.add(length)
} else {
self.into()
}
}
}
impl ops::Sub<Length> for $name {
type Output = Calc;
fn sub(self, rhs: Length) -> Self::Output {
Calc::from(<$target>::from(self)).sub(rhs)
}
}
impl ops::Sub<$name> for $name {
type Output = Calc;
fn sub(self, rhs: $name) -> Self::Output {
Calc::from(<$target>::from(self)).sub(<$target>::from(rhs))
}
}
impl<T> ops::Sub<Option<T>> for $name
where
$name: ops::Sub<T, Output = Calc>,
{
type Output = Calc;
fn sub(self, rhs: Option<T>) -> Self::Output {
if let Some(length) = rhs {
self.sub(length)
} else {
self.into()
}
}
}
impl ops::Mul<f32> for $name {
type Output = Calc;
fn mul(self, rhs: f32) -> Self::Output {
Calc::from(<$target>::from(self)).mul(rhs)
}
}
impl ops::Mul<i32> for $name {
type Output = Calc;
fn mul(self, rhs: i32) -> Self::Output {
Calc::from(<$target>::from(self)).mul(rhs as f32)
}
}
impl<T> ops::Mul<Option<T>> for $name
where
$name: ops::Mul<T, Output = Calc>,
{
type Output = Calc;
fn mul(self, rhs: Option<T>) -> Self::Output {
if let Some(length) = rhs {
self.mul(length)
} else {
self.into()
}
}
}
impl ops::Div<f32> for $name {
type Output = Calc;
fn div(self, rhs: f32) -> Self::Output {
Calc::from(<$target>::from(self)).div(rhs)
}
}
impl ops::Div<i32> for $name {
type Output = Calc;
fn div(self, rhs: i32) -> Self::Output {
Calc::from(<$target>::from(self)).div(rhs as f32)
}
}
impl<T> ops::Div<Option<T>> for $name
where
$name: ops::Div<T, Output = Calc>,
{
type Output = Calc;
fn div(self, rhs: Option<T>) -> Self::Output {
if let Some(length) = rhs {
self.div(length)
} else {
self.into()
}
}
}
};
($($name: ident: $css: literal),* $(,)?) => {
$(
units!(@one $name: $css);
)*
};
($(+fn -> Length { $($name: ident: $css: literal),* $(,)? }),* $(,)?) => {
$(
$(
units!(@one $name: $css);
units!(@two $name: $css -> Length);
units!(@three $name: $css -> Length);
impl From<$name> for Calc {
fn from(source: $name) -> Self {
Calc::Length(Box::new(Length::from(source)))
}
}
impl From<i32> for $name {
fn from(source: i32) -> Self {
$name(source as f32)
}
}
)*
)*
};
($(+fn -> Percent { $($name: ident: $css: literal),* $(,)? }),* $(,)?) => {
$(
$(
#[derive(Clone, Debug, Copy, PartialEq, PartialOrd, From)]
pub struct $name(f32);
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:.2}", self.0 * 100.0)?;
write!(f, $css)
}
}
units!(@two $name: $css -> Percent);
units!(@three $name: $css -> Percent);
impl From<Percent> for Calc {
fn from(source: Percent) -> Self {
Calc::Percent(source)
}
}
)*
)*
};
($(+fn -> $fn_ty: ty { $($name: ident: $css: literal),* $(,)? }),* $(,)?) => {
$(
$(
units!(@one $name: $css);
units!(@two $name: $css -> $fn_ty);
)*
)*
};
}
units! {
+fn -> Length {
Rem: "rem", Em: "em", Ex: "ex", Rex: "rex", Cap: "cap", Rcap: "rcap", Ch: "ch",
Rch: "rch", Ic: "ic", Ric: "ric", Lh: "lh", Rlh: "rlh",
Vw: "vw", Vh: "vh", Vi: "vi", Vb: "vb",
Vmin: "vmin", Vmax: "vmax",
Cm: "cm", Mm: "mm", Q: "q", Inch: "in",
Pc: "pc", Pt: "pt", Px: "px",
},
}
units! {
+fn -> Percent { Percent: "%" },
}
units! {
+fn -> Ms { Ms: "ms" },
+fn -> Sec { Sec: "s" },
}
units! {
Deg: "deg", Grad: "grad", Rad: "rad", Turn: "turn",
Hz: "Hz", KHz: "kHz",
Dpi: "dpi", Dpcm: "dpcm", Dppx: "dppx",
}
#[derive(Clone, Debug, PartialOrd, PartialEq, Display, From)]
pub enum Length {
#[from]
Em(Em),
#[from]
Rem(Rem),
#[from]
Ex(Ex),
#[from]
Rex(Rex),
#[from]
Cap(Cap),
#[from]
Rcap(Rcap),
#[from]
Ch(Ch),
#[from]
Rch(Rch),
#[from]
Ic(Ic),
#[from]
Ric(Ric),
#[from]
Lh(Lh),
#[from]
Rlh(Rlh),
#[from]
Vw(Vw),
#[from]
Vh(Vh),
#[from]
Vi(Vi),
#[from]
Vb(Vb),
#[from]
Vmin(Vmin),
#[from]
Vmax(Vmax),
#[from]
Cm(Cm),
#[from]
Mm(Mm),
#[from]
Q(Q),
#[from]
In(Inch),
#[from]
Pc(Pc),
#[from]
Pt(Pt),
#[from]
Px(Px),
#[from]
Calc(Calc),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum LengthPercent {
#[from]
Length(Length),
#[from(forward)]
Percent(Percent),
#[from]
Calc(Calc),
}
impl Unit for LengthPercent {
fn zero() -> Self {
0.0.into()
}
fn half() -> Self {
0.5.into()
}
fn full() -> Self {
1.0.into()
}
}