1use css_parse::{Build, Cursor, Parser, Peek, T, ToNumberValue};
2use csskit_derives::{IntoCursor, ToCursors, Visitable};
3
4#[derive(IntoCursor, ToCursors, Visitable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
7#[visit(self)]
8pub enum Time {
9 Zero(T![Number]),
10 Ms(T![Dimension::Ms]),
11 S(T![Dimension::S]),
12}
13
14impl From<Time> for f32 {
15 fn from(val: Time) -> Self {
16 match val {
17 Time::Zero(_) => 0.0,
18 Time::Ms(f) => f.into(),
19 Time::S(f) => f.into(),
20 }
21 }
22}
23
24impl ToNumberValue for Time {
25 fn to_number_value(&self) -> Option<f32> {
26 Some((*self).into())
27 }
28}
29
30impl<'a> Peek<'a> for Time {
31 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
32 (<T![Number]>::peek(p, c) && c.token().value() == 0.0)
33 || <T![Dimension::Ms]>::peek(p, c)
34 || <T![Dimension::S]>::peek(p, c)
35 }
36}
37
38impl<'a> Build<'a> for Time {
39 fn build(p: &Parser<'a>, c: Cursor) -> Self {
40 if <T![Number]>::peek(p, c) && c.token().value() == 0.0 {
41 Self::Zero(<T![Number]>::build(p, c))
42 } else if <T![Dimension::S]>::peek(p, c) {
43 Self::S(<T![Dimension::S]>::build(p, c))
44 } else {
45 Self::Ms(<T![Dimension::Ms]>::build(p, c))
46 }
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use css_parse::{assert_parse, assert_parse_error};
54
55 #[test]
56 fn size_test() {
57 assert_eq!(std::mem::size_of::<Time>(), 16);
58 }
59
60 #[test]
61 fn test_writes() {
62 assert_parse!(Time, "0");
63 assert_parse!(Time, "0s");
64 assert_parse!(Time, "0ms");
65 }
66
67 #[test]
68 fn test_errors() {
69 assert_parse_error!(Time, "1");
70 assert_parse_error!(Time, "foo");
71 }
72}