1use super::angle::impl_try_from_angle;
4use super::calc::Calc;
5use super::number::CSSNumber;
6use crate::error::{ParserError, PrinterError};
7use crate::printer::Printer;
8use crate::traits::private::AddInternal;
9use crate::traits::{impl_op, Map, Op, Parse, Sign, ToCss, Zero};
10#[cfg(feature = "visitor")]
11use crate::visitor::Visit;
12use cssparser::*;
13
14#[derive(Debug, Clone, PartialEq)]
20#[cfg_attr(feature = "visitor", derive(Visit))]
21#[cfg_attr(feature = "visitor", visit(visit_time, TIMES))]
22#[cfg_attr(
23 feature = "serde",
24 derive(serde::Serialize, serde::Deserialize),
25 serde(tag = "type", content = "value", rename_all = "kebab-case")
26)]
27#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
28#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
29pub enum Time {
30 Seconds(CSSNumber),
32 Milliseconds(CSSNumber),
34}
35
36impl Time {
37 pub fn to_ms(&self) -> CSSNumber {
39 match self {
40 Time::Seconds(s) => s * 1000.0,
41 Time::Milliseconds(ms) => *ms,
42 }
43 }
44}
45
46impl Zero for Time {
47 fn zero() -> Self {
48 Time::Milliseconds(0.0)
49 }
50
51 fn is_zero(&self) -> bool {
52 match self {
53 Time::Seconds(s) => s.is_zero(),
54 Time::Milliseconds(s) => s.is_zero(),
55 }
56 }
57}
58
59impl<'i> Parse<'i> for Time {
60 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
61 match input.try_parse(Calc::parse) {
62 Ok(Calc::Value(v)) => return Ok(*v),
63 Ok(_) => return Err(input.new_custom_error(ParserError::InvalidValue)),
65 _ => {}
66 }
67
68 let location = input.current_source_location();
69 match *input.next()? {
70 Token::Dimension { value, ref unit, .. } => {
71 match_ignore_ascii_case! { unit,
72 "s" => Ok(Time::Seconds(value)),
73 "ms" => Ok(Time::Milliseconds(value)),
74 _ => Err(location.new_unexpected_token_error(Token::Ident(unit.clone())))
75 }
76 }
77 ref t => Err(location.new_unexpected_token_error(t.clone())),
78 }
79 }
80}
81
82impl<'i> TryFrom<&Token<'i>> for Time {
83 type Error = ();
84
85 fn try_from(token: &Token) -> Result<Self, Self::Error> {
86 match token {
87 Token::Dimension { value, ref unit, .. } => match_ignore_ascii_case! { unit,
88 "s" => Ok(Time::Seconds(*value)),
89 "ms" => Ok(Time::Milliseconds(*value)),
90 _ => Err(()),
91 },
92 _ => Err(()),
93 }
94 }
95}
96
97impl ToCss for Time {
98 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
99 where
100 W: std::fmt::Write,
101 {
102 match self {
105 Time::Seconds(s) => {
106 if *s > 0.0 && *s < 0.1 {
107 (*s * 1000.0).to_css(dest)?;
108 dest.write_str("ms")
109 } else {
110 s.to_css(dest)?;
111 dest.write_str("s")
112 }
113 }
114 Time::Milliseconds(ms) => {
115 if *ms == 0.0 || *ms >= 100.0 {
116 (*ms / 1000.0).to_css(dest)?;
117 dest.write_str("s")
118 } else {
119 ms.to_css(dest)?;
120 dest.write_str("ms")
121 }
122 }
123 }
124 }
125}
126
127impl std::convert::Into<Calc<Time>> for Time {
128 fn into(self) -> Calc<Time> {
129 Calc::Value(Box::new(self))
130 }
131}
132
133impl std::convert::From<Calc<Time>> for Time {
134 fn from(calc: Calc<Time>) -> Time {
135 match calc {
136 Calc::Value(v) => *v,
137 _ => unreachable!(),
138 }
139 }
140}
141
142impl std::ops::Mul<f32> for Time {
143 type Output = Self;
144
145 fn mul(self, other: f32) -> Time {
146 match self {
147 Time::Seconds(t) => Time::Seconds(t * other),
148 Time::Milliseconds(t) => Time::Milliseconds(t * other),
149 }
150 }
151}
152
153impl AddInternal for Time {
154 fn add(self, other: Self) -> Self {
155 self + other
156 }
157}
158
159impl std::cmp::PartialOrd<Time> for Time {
160 fn partial_cmp(&self, other: &Time) -> Option<std::cmp::Ordering> {
161 self.to_ms().partial_cmp(&other.to_ms())
162 }
163}
164
165impl Op for Time {
166 fn op<F: FnOnce(f32, f32) -> f32>(&self, to: &Self, op: F) -> Self {
167 match (self, to) {
168 (Time::Seconds(a), Time::Seconds(b)) => Time::Seconds(op(*a, *b)),
169 (Time::Milliseconds(a), Time::Milliseconds(b)) => Time::Milliseconds(op(*a, *b)),
170 (Time::Seconds(a), Time::Milliseconds(b)) => Time::Seconds(op(*a, b / 1000.0)),
171 (Time::Milliseconds(a), Time::Seconds(b)) => Time::Milliseconds(op(*a, b * 1000.0)),
172 }
173 }
174
175 fn op_to<T, F: FnOnce(f32, f32) -> T>(&self, rhs: &Self, op: F) -> T {
176 match (self, rhs) {
177 (Time::Seconds(a), Time::Seconds(b)) => op(*a, *b),
178 (Time::Milliseconds(a), Time::Milliseconds(b)) => op(*a, *b),
179 (Time::Seconds(a), Time::Milliseconds(b)) => op(*a, b / 1000.0),
180 (Time::Milliseconds(a), Time::Seconds(b)) => op(*a, b * 1000.0),
181 }
182 }
183}
184
185impl Map for Time {
186 fn map<F: FnOnce(f32) -> f32>(&self, op: F) -> Self {
187 match self {
188 Time::Seconds(t) => Time::Seconds(op(*t)),
189 Time::Milliseconds(t) => Time::Milliseconds(op(*t)),
190 }
191 }
192}
193
194impl Sign for Time {
195 fn sign(&self) -> f32 {
196 match self {
197 Time::Seconds(v) | Time::Milliseconds(v) => v.sign(),
198 }
199 }
200}
201
202impl_op!(Time, std::ops::Rem, rem);
203impl_op!(Time, std::ops::Add, add);
204
205impl_try_from_angle!(Time);