css_style/
unit.rs

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        // Add
34        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        // Sub
66        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        // Mul
98        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        // Div
130        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        // https://www.w3.org/TR/css-values-4/#font-relative-lengths Em: "em",
229        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        // https://www.w3.org/TR/css-values-4/#viewport-relative-lengths
233        Vw: "vw", Vh: "vh", Vi: "vi", Vb: "vb",
234        Vmin: "vmin", Vmax: "vmax",
235
236        // https://www.w3.org/TR/css-values-4/#absolute-lengths
237        Cm: "cm", Mm: "mm", Q: "q", Inch: "in",
238        Pc: "pc", Pt: "pt", Px: "px",
239    },
240}
241
242units! {
243    // https://www.w3.org/TR/css-values-4/#percentages
244    +fn -> Percent { Percent: "%" },
245}
246
247units! {
248    // https://www.w3.org/TR/css-values-4/#time
249    +fn -> Ms { Ms: "ms" },
250    +fn -> Sec { Sec: "s" },
251}
252
253units! {
254    // https://www.w3.org/TR/css-values-4/#angles
255    Deg: "deg", Grad: "grad", Rad: "rad", Turn: "turn",
256
257    // https://www.w3.org/TR/css-values-4/#frequency
258    Hz: "Hz", KHz: "kHz",
259
260    // https://www.w3.org/TR/css-values-4/#resolution
261    Dpi: "dpi", Dpcm: "dpcm", Dppx: "dppx",
262
263}
264
265// sum types
266
267#[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}