1use crate::calc::Calc;
2use paste::paste;
3use std::{fmt, ops};
4
5pub trait Unit {
6 fn zero() -> Self;
7 fn half() -> Self;
8 fn full() -> Self;
9}
10
11macro_rules! units {
12 (@one $name: ident: $css: literal) => {
13 #[derive(Clone, Debug, Copy, PartialEq, PartialOrd, From)]
14 pub struct $name(f32);
15
16 impl fmt::Display for $name {
17 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18 write!(f, "{:.2}", self.0)?;
19 write!(f, $css)
20 }
21 }
22 };
23
24 (@two $name: ident: $css: literal -> $fn_ty: ty) => {
25 paste! {
26 pub fn [<$name:snake>](value: impl Into<$name>) -> $fn_ty {
27 (value.into()).into()
28 }
29 }
30 };
31
32 (@three $name: ident: $css: literal -> $target: ty) => {
33 impl ops::Add<Length> for $name {
35 type Output = Calc;
36
37 fn add(self, rhs: Length) -> Self::Output {
38 Calc::from(<$target>::from(self)).add(rhs)
39 }
40 }
41
42 impl ops::Add<$name> for $name {
43 type Output = Calc;
44
45 fn add(self, rhs: $name) -> Self::Output {
46 Calc::from(<$target>::from(self)).add(<$target>::from(rhs))
47 }
48 }
49
50 impl<T> ops::Add<Option<T>> for $name
51 where
52 $name: ops::Add<T, Output = Calc>,
53 {
54 type Output = Calc;
55
56 fn add(self, rhs: Option<T>) -> Self::Output {
57 if let Some(length) = rhs {
58 self.add(length)
59 } else {
60 self.into()
61 }
62 }
63 }
64
65 impl ops::Sub<Length> for $name {
67 type Output = Calc;
68
69 fn sub(self, rhs: Length) -> Self::Output {
70 Calc::from(<$target>::from(self)).sub(rhs)
71 }
72 }
73
74 impl ops::Sub<$name> for $name {
75 type Output = Calc;
76
77 fn sub(self, rhs: $name) -> Self::Output {
78 Calc::from(<$target>::from(self)).sub(<$target>::from(rhs))
79 }
80 }
81
82 impl<T> ops::Sub<Option<T>> for $name
83 where
84 $name: ops::Sub<T, Output = Calc>,
85 {
86 type Output = Calc;
87
88 fn sub(self, rhs: Option<T>) -> Self::Output {
89 if let Some(length) = rhs {
90 self.sub(length)
91 } else {
92 self.into()
93 }
94 }
95 }
96
97 impl ops::Mul<f32> for $name {
99 type Output = Calc;
100
101 fn mul(self, rhs: f32) -> Self::Output {
102 Calc::from(<$target>::from(self)).mul(rhs)
103 }
104 }
105
106 impl ops::Mul<i32> for $name {
107 type Output = Calc;
108
109 fn mul(self, rhs: i32) -> Self::Output {
110 Calc::from(<$target>::from(self)).mul(rhs as f32)
111 }
112 }
113
114 impl<T> ops::Mul<Option<T>> for $name
115 where
116 $name: ops::Mul<T, Output = Calc>,
117 {
118 type Output = Calc;
119
120 fn mul(self, rhs: Option<T>) -> Self::Output {
121 if let Some(length) = rhs {
122 self.mul(length)
123 } else {
124 self.into()
125 }
126 }
127 }
128
129 impl ops::Div<f32> for $name {
131 type Output = Calc;
132
133 fn div(self, rhs: f32) -> Self::Output {
134 Calc::from(<$target>::from(self)).div(rhs)
135 }
136 }
137
138 impl ops::Div<i32> for $name {
139 type Output = Calc;
140
141 fn div(self, rhs: i32) -> Self::Output {
142 Calc::from(<$target>::from(self)).div(rhs as f32)
143 }
144 }
145
146 impl<T> ops::Div<Option<T>> for $name
147 where
148 $name: ops::Div<T, Output = Calc>,
149 {
150 type Output = Calc;
151
152 fn div(self, rhs: Option<T>) -> Self::Output {
153 if let Some(length) = rhs {
154 self.div(length)
155 } else {
156 self.into()
157 }
158 }
159 }
160 };
161
162
163 ($($name: ident: $css: literal),* $(,)?) => {
164 $(
165 units!(@one $name: $css);
166 )*
167 };
168
169 ($(+fn -> Length { $($name: ident: $css: literal),* $(,)? }),* $(,)?) => {
170 $(
171 $(
172 units!(@one $name: $css);
173 units!(@two $name: $css -> Length);
174 units!(@three $name: $css -> Length);
175
176 impl From<$name> for Calc {
177 fn from(source: $name) -> Self {
178 Calc::Length(Box::new(Length::from(source)))
179 }
180 }
181
182 impl From<i32> for $name {
183 fn from(source: i32) -> Self {
184 $name(source as f32)
185 }
186 }
187 )*
188 )*
189 };
190
191 ($(+fn -> Percent { $($name: ident: $css: literal),* $(,)? }),* $(,)?) => {
192 $(
193 $(
194 #[derive(Clone, Debug, Copy, PartialEq, PartialOrd, From)]
195 pub struct $name(f32);
196
197 impl fmt::Display for $name {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 write!(f, "{:.2}", self.0 * 100.0)?;
200 write!(f, $css)
201 }
202 }
203
204 units!(@two $name: $css -> Percent);
205 units!(@three $name: $css -> Percent);
206
207 impl From<Percent> for Calc {
208 fn from(source: Percent) -> Self {
209 Calc::Percent(source)
210 }
211 }
212 )*
213 )*
214 };
215
216 ($(+fn -> $fn_ty: ty { $($name: ident: $css: literal),* $(,)? }),* $(,)?) => {
217 $(
218 $(
219 units!(@one $name: $css);
220 units!(@two $name: $css -> $fn_ty);
221 )*
222 )*
223 };
224}
225
226units! {
227 +fn -> Length {
228 Rem: "rem", Em: "em", Ex: "ex", Rex: "rex", Cap: "cap", Rcap: "rcap", Ch: "ch",
230 Rch: "rch", Ic: "ic", Ric: "ric", Lh: "lh", Rlh: "rlh",
231
232 Vw: "vw", Vh: "vh", Vi: "vi", Vb: "vb",
234 Vmin: "vmin", Vmax: "vmax",
235
236 Cm: "cm", Mm: "mm", Q: "q", Inch: "in",
238 Pc: "pc", Pt: "pt", Px: "px",
239 },
240}
241
242units! {
243 +fn -> Percent { Percent: "%" },
245}
246
247units! {
248 +fn -> Ms { Ms: "ms" },
250 +fn -> Sec { Sec: "s" },
251}
252
253units! {
254 Deg: "deg", Grad: "grad", Rad: "rad", Turn: "turn",
256
257 Hz: "Hz", KHz: "kHz",
259
260 Dpi: "dpi", Dpcm: "dpcm", Dppx: "dppx",
262
263}
264
265#[derive(Clone, Debug, PartialOrd, PartialEq, Display, From)]
268pub enum Length {
269 #[from]
270 Em(Em),
271 #[from]
272 Rem(Rem),
273 #[from]
274 Ex(Ex),
275 #[from]
276 Rex(Rex),
277 #[from]
278 Cap(Cap),
279 #[from]
280 Rcap(Rcap),
281 #[from]
282 Ch(Ch),
283 #[from]
284 Rch(Rch),
285 #[from]
286 Ic(Ic),
287 #[from]
288 Ric(Ric),
289 #[from]
290 Lh(Lh),
291 #[from]
292 Rlh(Rlh),
293
294 #[from]
295 Vw(Vw),
296 #[from]
297 Vh(Vh),
298 #[from]
299 Vi(Vi),
300 #[from]
301 Vb(Vb),
302 #[from]
303 Vmin(Vmin),
304 #[from]
305 Vmax(Vmax),
306
307 #[from]
308 Cm(Cm),
309 #[from]
310 Mm(Mm),
311 #[from]
312 Q(Q),
313 #[from]
314 In(Inch),
315 #[from]
316 Pc(Pc),
317 #[from]
318 Pt(Pt),
319 #[from]
320 Px(Px),
321
322 #[from]
323 Calc(Calc),
324}
325
326#[derive(Clone, Debug, PartialEq, Display, From)]
327pub enum LengthPercent {
328 #[from]
329 Length(Length),
330 #[from(forward)]
331 Percent(Percent),
332 #[from]
333 Calc(Calc),
334}
335
336impl Unit for LengthPercent {
337 fn zero() -> Self {
338 0.0.into()
339 }
340
341 fn half() -> Self {
342 0.5.into()
343 }
344
345 fn full() -> Self {
346 1.0.into()
347 }
348}