1use num_traits::cast::AsPrimitive;
2
3pub type F32 = ordered_float::NotNan<f32>;
4
5#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, strum::Display, PartialOrd, Ord)]
6pub enum Operator {
7 #[strum(to_string = "+")] Plus,
8 #[strum(to_string = "-")] Minus,
9}
10
11#[derive(Debug, PartialEq, Eq, Hash, Clone, Default, PartialOrd, Ord)]
12pub enum Unit {
13 #[default]
14 Zero,
15 Px(F32),
16 Em(F32),
17 Rem(F32),
18 Vw(F32),
19 Vh(F32),
20 Vmin(F32),
21 Vmax(F32),
22 Fr(F32),
23 Percent(F32),
24 Duration(F32), Calc(Box<Unit>, Operator, Box<Unit>),
27}
28
29impl Unit {
30 #[inline] pub fn calc(left: Self, op: Operator, right: Self) -> Self { Self::Calc(Box::new(left), op, Box::new(right)) }
31 #[inline] pub fn px<T: AsPrimitive<f32>>(x: T) -> Self { Self::Px( F32::new(x.as_()).unwrap()) }
32 #[inline] pub fn em<T: AsPrimitive<f32>>(x: T) -> Self { Self::Em( F32::new(x.as_()).unwrap()) }
33 #[inline] pub fn rem<T: AsPrimitive<f32>>(x: T) -> Self { Self::Rem( F32::new(x.as_()).unwrap()) }
34 #[inline] pub fn vw<T: AsPrimitive<f32>>(x: T) -> Self { Self::Vw( F32::new(x.as_()).unwrap()) }
35 #[inline] pub fn vh<T: AsPrimitive<f32>>(x: T) -> Self { Self::Vh( F32::new(x.as_()).unwrap()) }
36 #[inline] pub fn vmin<T: AsPrimitive<f32>>(x: T) -> Self { Self::Vmin( F32::new(x.as_()).unwrap()) }
37 #[inline] pub fn vmax<T: AsPrimitive<f32>>(x: T) -> Self { Self::Vmax( F32::new(x.as_()).unwrap()) }
38 #[inline] pub fn fr<T: AsPrimitive<f32>>(x: T) -> Self { Self::Fr( F32::new(x.as_()).unwrap()) }
39 #[inline] pub fn pct<T: AsPrimitive<f32>>(x: T) -> Self { Self::Percent( F32::new(x.as_()).unwrap()) }
40 #[inline] pub fn dur<T: AsPrimitive<f32>>(x: T) -> Self { Self::Duration(F32::new(x.as_()).unwrap()) }
41}
42
43#[rustfmt::skip]
44impl std::fmt::Display for Unit {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 Self::Zero => "0".fmt(f),
48 Self::Px(x) => write!(f, "{}px", x),
49 Self::Em(x) => write!(f, "{}em", x),
50 Self::Rem(x) => write!(f, "{}rem", x),
51 Self::Vw(x) => write!(f, "{}vw", x),
52 Self::Vh(x) => write!(f, "{}vh", x),
53 Self::Vmin(x) => write!(f, "{}vmin", x),
54 Self::Vmax(x) => write!(f, "{}vmax", x),
55 Self::Fr(x) => write!(f, "{}fr", x),
56 Self::Percent(x) => write!(f, "{}%", x),
57 Self::Duration(x) => write!(f, "{}ms", x),
58 Self::Calc(left, op, right) => write!(f, "calc({} {} {})", left, op, right),
59 }
60 }
61}
62
63impl std::ops::Add for Unit {
64 type Output = Self;
65
66 fn add(self, rhs: Self) -> Self {
67 match (self, rhs) {
68 (Self::Zero, Self::Zero) => Self::Zero,
69 (Self::Px(a), Self::Px(b)) => Self::Px(a + b),
70 (Self::Em(a), Self::Em(b)) => Self::Em(a + b),
71 (Self::Rem(a), Self::Rem(b)) => Self::Rem(a + b),
72 (Self::Vw(a), Self::Vw(b)) => Self::Vw(a + b),
73 (Self::Vh(a), Self::Vh(b)) => Self::Vh(a + b),
74 (Self::Vmin(a), Self::Vmin(b)) => Self::Vmin(a + b),
75 (Self::Vmax(a), Self::Vmax(b)) => Self::Vmax(a + b),
76 (Self::Fr(a), Self::Fr(b)) => Self::Fr(a + b),
77 (Self::Percent(a), Self::Percent(b)) => Self::Percent(a + b),
78 (Self::Duration(a), Self::Duration(b)) => Self::Duration(a + b),
79 (a, b) => Self::Calc(Box::new(a), Operator::Plus, Box::new(b)),
80 }
81 }
82}
83
84impl std::ops::Sub for Unit {
85 type Output = Self;
86
87 fn sub(self, rhs: Self) -> Self {
88 match (self, rhs) {
89 (Self::Zero, Self::Zero) => Self::Zero,
90 (Self::Px(a), Self::Px(b)) => Self::Px(a - b),
91 (Self::Em(a), Self::Em(b)) => Self::Em(a - b),
92 (Self::Rem(a), Self::Rem(b)) => Self::Rem(a - b),
93 (Self::Vw(a), Self::Vw(b)) => Self::Vw(a - b),
94 (Self::Vh(a), Self::Vh(b)) => Self::Vh(a - b),
95 (Self::Vmin(a), Self::Vmin(b)) => Self::Vmin(a - b),
96 (Self::Vmax(a), Self::Vmax(b)) => Self::Vmax(a - b),
97 (Self::Fr(a), Self::Fr(b)) => Self::Fr(a - b),
98 (Self::Percent(a), Self::Percent(b)) => Self::Percent(a - b),
99 (Self::Duration(a), Self::Duration(b)) => Self::Duration(a - b),
100 (a, b) => Self::Calc(Box::new(a), Operator::Minus, Box::new(b)),
101 }
102 }
103}
104
105#[rustfmt::skip]
107#[macro_export]
108macro_rules! unit {
109 (0) => { $crate::units::Unit::Zero };
110
111 ($e:literal $(px)?) => { $crate::units::Unit::px($e) };
112 ($e:literal ms) => { $crate::units::Unit::dur($e) };
113 ($e:literal $frag:ident) => { $crate::units::Unit::$frag($e) };
114 ($e:literal %) => { $crate::units::Unit::pct($e) };
115
116 ($e:ident $(px)?) => { $crate::units::Unit::px($e) };
117 ($e:ident ms) => { $crate::units::Unit::dur($e) };
118 ($e:ident $frag:ident) => { $crate::units::Unit::$frag($e) };
119 ($e:ident %) => { $crate::units::Unit::pct($e) };
120
121 (($($e:tt)+) $(px)?) => { $crate::units::Unit::px($($e)+) };
123 (($($e:tt)+) ms) => { $crate::units::Unit::dur($($e)+) };
124 (($($e:tt)+) $frag:ident) => { $crate::units::Unit::$frag($($e)+) };
125 (($($e:tt)+) %) => { $crate::units::Unit::pct($($e)+) };
126
127 ($e1:literal $frag1:tt + $e2:literal $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Plus, $crate::unit!($e2 $frag2)) };
129 ($e1:ident $frag1:tt + $e2:literal $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Plus, $crate::unit!($e2 $frag2)) };
130 (($($e1:tt)+) $frag1:tt + $e2:literal $frag2:tt) => { $crate::units::Unit::calc($crate::unit!(($($e1)+) $frag1), $crate::units::Operator::Plus, $crate::unit!($e2 $frag2)) };
131 ($e1:literal $frag1:tt + $e2:ident $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Plus, $crate::unit!($e2 $frag2)) };
132 ($e1:ident $frag1:tt + $e2:ident $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Plus, $crate::unit!($e2 $frag2)) };
133 (($($e1:tt)+) $frag1:tt + $e2:ident $frag2:tt) => { $crate::units::Unit::calc($crate::unit!(($($e1)+) $frag1), $crate::units::Operator::Plus, $crate::unit!($e2 $frag2)) };
134 ($e1:literal $frag1:tt + ($($e2:tt)+) $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Plus, $crate::unit!(($($e2)+) $frag2)) };
135 ($e1:ident $frag1:tt + ($($e2:tt)+) $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Plus, $crate::unit!(($($e2)+) $frag2)) };
136 (($($e1:tt)+) $frag1:tt + ($($e2:tt)+) $frag2:tt) => { $crate::units::Unit::calc($crate::unit!(($($e1)+) $frag1), $crate::units::Operator::Plus, $crate::unit!(($($e2)+) $frag2)) };
137
138 ($e1:literal $frag1:tt - $e2:literal $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Minus, $crate::unit!($e2 $frag2)) };
139 ($e1:ident $frag1:tt - $e2:literal $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Minus, $crate::unit!($e2 $frag2)) };
140 (($($e1:tt)+) $frag1:tt - $e2:literal $frag2:tt) => { $crate::units::Unit::calc($crate::unit!(($($e1)+) $frag1), $crate::units::Operator::Minus, $crate::unit!($e2 $frag2)) };
141 ($e1:literal $frag1:tt - $e2:ident $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Minus, $crate::unit!($e2 $frag2)) };
142 ($e1:ident $frag1:tt - $e2:ident $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Minus, $crate::unit!($e2 $frag2)) };
143 (($($e1:tt)+) $frag1:tt - $e2:ident $frag2:tt) => { $crate::units::Unit::calc($crate::unit!(($($e1)+) $frag1), $crate::units::Operator::Minus, $crate::unit!($e2 $frag2)) };
144 ($e1:literal $frag1:tt - ($($e2:tt)+) $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Minus, $crate::unit!(($($e2)+) $frag2)) };
145 ($e1:ident $frag1:tt - ($($e2:tt)+) $frag2:tt) => { $crate::units::Unit::calc($crate::unit!($e1 $frag1), $crate::units::Operator::Minus, $crate::unit!(($($e2)+) $frag2)) };
146 (($($e1:tt)+) $frag1:tt - ($($e2:tt)+) $frag2:tt) => { $crate::units::Unit::calc($crate::unit!(($($e1)+) $frag1), $crate::units::Operator::Minus, $crate::unit!(($($e2)+) $frag2)) };
147}