use std::{fmt, ops};
use crate::{
margin, padding, position, size,
unit::{Length, Percent},
};
use paste::paste;
#[derive(Debug, PartialOrd, PartialEq, Clone)]
pub enum Calc {
Length(Box<Length>),
Percent(Percent),
CssVariable(String),
Sum(Box<Calc>, Box<Calc>),
Sub(Box<Calc>, Box<Calc>),
Mul(Box<Calc>, f32),
Mul2(f32, Box<Calc>),
Div(Box<Calc>, f32),
}
impl From<Length> for Calc {
fn from(source: Length) -> Self {
Calc::Length(Box::new(source))
}
}
impl TryFrom<padding::Length> for Calc {
type Error = &'static str;
fn try_from(value: padding::Length) -> Result<Self, Self::Error> {
match value {
padding::Length::Length(len) => Ok(len.into()),
padding::Length::Percent(per) => Ok(per.into()),
padding::Length::Inherit => Err("Calc cannot accept inherit as value"),
}
}
}
impl TryFrom<margin::Length> for Calc {
type Error = &'static str;
fn try_from(value: margin::Length) -> Result<Self, Self::Error> {
match value {
margin::Length::Length(len) => Ok(len.into()),
margin::Length::Percent(per) => Ok(per.into()),
_ => Err("Calc cannot accept values such as inherit, auto ..etc"),
}
}
}
impl TryFrom<size::Length> for Calc {
type Error = &'static str;
fn try_from(value: size::Length) -> Result<Self, Self::Error> {
match value {
size::Length::Length(len) => Ok(len.into()),
size::Length::Percent(per) => Ok(per.into()),
_ => Err("Calc cannot accept values such as inherit, auto ..etc"),
}
}
}
impl TryFrom<position::PostionLength> for Calc {
type Error = &'static str;
fn try_from(value: position::PostionLength) -> Result<Self, Self::Error> {
match value {
position::PostionLength::Length(len) => Ok(len.into()),
position::PostionLength::Percent(per) => Ok(per.into()),
_ => Err("Calc cannot accept values such as inherit, auto ..etc"),
}
}
}
impl From<String> for Calc {
fn from(source: String) -> Self {
Calc::CssVariable(source)
}
}
impl From<&str> for Calc {
fn from(source: &str) -> Self {
Calc::CssVariable(source.to_string())
}
}
impl fmt::Display for Calc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt_calc(calc: &Calc) -> String {
match calc {
Calc::Length(val) => val.to_string(),
Calc::Percent(val) => val.to_string(),
Calc::CssVariable(val) => format!("var({})", val.to_string()),
Calc::Sum(c, val) => format!("({} + {})", fmt_calc(c), fmt_calc(val)),
Calc::Sub(c, val) => format!("({} - {})", fmt_calc(c), fmt_calc(val)),
Calc::Mul(c, val) => format!("({} * {})", fmt_calc(c), val),
Calc::Mul2(val, c) => format!("({} * {})", val, fmt_calc(c)),
Calc::Div(c, val) => format!("({} / {})", fmt_calc(c), val),
}
}
match self {
Self::Length(val) => write!(f, "{}", val),
Self::Percent(val) => write!(f, "{}", val),
Self::CssVariable(val) => write!(f, "calc(var({}))", val),
_ => write!(f, "calc{}", fmt_calc(self)),
}
}
}
macro_rules! ops_impl {
($($op: ident<$($t: ty),*> for $target: ty => { $e: expr }),* $(,)?) => {
$(
$(
paste! {
impl ops::$op<$t> for $target {
type Output = Calc;
fn [<$op:snake>](self, rhs: $t) -> Calc {
$e(self, rhs)
}
}
}
)*
)*
};
($($op: ident<$($t: ty),*> and Optional for $target: ty => { $e: expr }),* $(,)?) => {
$(
$(
paste! {
impl ops::$op<$t> for $target {
type Output = Calc;
fn [<$op:snake>](self, rhs: $t) -> Calc {
$e(self, rhs)
}
}
}
)*
impl<T> ops::$op<Option<T>> for $target
where
$target: ops::$op<T, Output = Calc>,
{
type Output = Calc;
paste! {
fn [<$op:snake>](self, rhs: Option<T>) -> Self::Output {
if let Some(length) = rhs {
self.[<$op:snake>](length)
} else {
self.into()
}
}
}
}
)*
};
}
ops_impl!(
Add<Calc, Percent, Length> and Optional for Calc => { |c, rhs| Self::Sum(Box::new(c), Box::new(Calc::from(rhs))) },
Sub<Calc, Percent, Length> and Optional for Calc => { |c, rhs| Self::Sub(Box::new(c), Box::new(Calc::from(rhs))) },
Mul<f32, i32, i16, i8, u32, u16, u8, isize, usize> and Optional for Calc => { |c, val| Self::Mul(Box::new(c), val as f32) },
Div<f32, i32, i16, i8, u32, u16, u8, isize, usize> and Optional for Calc => { |c, val| Self::Div(Box::new(c), val as f32) },
);
ops_impl!(
Mul<Calc> for f32 => { |val, c| Calc::Mul2(val, Box::new(c)) },
Mul<Calc> for i32 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Calc> for i16 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Calc> for i8 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Calc> for u32 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Calc> for u16 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Calc> for u8 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Calc> for isize => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Calc> for usize => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
Mul<Length> for f32 => { |val, length| Calc::Mul2(val, Box::new(Calc::from(length))) },
Mul<Length> for i32 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Length> for i16 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Length> for i8 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Length> for u32 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Length> for u16 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Length> for u8 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Length> for isize => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Length> for usize => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
Mul<Percent> for f32 => { |val, pct| Calc::Mul2(val, Box::new(Calc::from(pct))) },
Mul<Percent> for i32 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Mul<Percent> for i16 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Mul<Percent> for i8 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Mul<Percent> for u32 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Mul<Percent> for u16 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Mul<Percent> for u8 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Mul<Percent> for isize => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Mul<Percent> for usize => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
Add<Calc, Percent, Length> for Length => { |length, rhs| Calc::from(length).add(rhs) },
Sub<Calc, Percent, Length> for Length => { |length, rhs| Calc::from(length).sub(rhs) },
Mul<f32, i32, i16, i8, u32, u16, u8, isize, usize> for Length => { |length, val| Calc::from(length).mul(val as f32) },
Div<f32, i32, i16, i8, u32, u16, u8, isize, usize> for Length => { |length, val| Calc::from(length).div(val as f32) },
);
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum CalcArgument {
Length(Length),
Percent(Percent),
CssVariable(String),
}
impl From<&str> for CalcArgument {
fn from(source: &str) -> Self {
CalcArgument::CssVariable(source.to_string())
}
}
pub fn calc(value: impl Into<CalcArgument>) -> Calc {
match value.into() {
CalcArgument::Length(len) => Calc::Length(Box::new(len)),
CalcArgument::Percent(per) => Calc::Percent(per),
CalcArgument::CssVariable(var) => Calc::CssVariable(var),
}
}