Skip to main content

stylex_css_parser/css_types/
time.rs

1/*!
2CSS Time type parsing.
3
4Handles time values with 's' (seconds) and 'ms' (milliseconds) units.
5*/
6
7use crate::{token_parser::TokenParser, token_types::SimpleToken};
8use std::fmt::{self, Display};
9
10/// Valid time units
11pub const TIME_UNITS: &[&str] = &["s", "ms"];
12
13/// CSS Time value with unit
14#[derive(Debug, Clone, PartialEq)]
15pub struct Time {
16  pub value: f32,
17  pub unit: String, // "s" or "ms"
18}
19
20impl Time {
21  /// Create a new Time value
22  pub fn new(value: f32, unit: String) -> Self {
23    Self { value, unit }
24  }
25
26  /// All valid time units
27  pub fn units() -> &'static [&'static str] {
28    TIME_UNITS
29  }
30
31  /// Check if a unit is a valid time unit
32  pub fn is_valid_unit(unit: &str) -> bool {
33    TIME_UNITS.contains(&unit)
34  }
35
36  /// Parser for CSS time values
37  pub fn parser() -> TokenParser<Time> {
38    TokenParser::<SimpleToken>::token(
39      SimpleToken::Dimension {
40        value: 0.0,
41        unit: String::new(),
42      },
43      Some("Dimension"),
44    )
45    .map(
46      |token| {
47        if let SimpleToken::Dimension { value, unit } = token {
48          if Self::is_valid_unit(&unit) {
49            Some((value as f32, unit))
50          } else {
51            None
52          }
53        } else {
54          None
55        }
56      },
57      Some("extract_time_dimension"),
58    )
59    .where_fn(|opt| opt.is_some(), Some("valid_time"))
60    .map(
61      |opt| {
62        let (value, unit) = opt.unwrap();
63        Time::new(value, unit)
64      },
65      Some("to_time"),
66    )
67  }
68}
69
70impl Display for Time {
71  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72    if self.unit == "ms" {
73      write!(f, "{}s", self.value / 1000.0)
74    } else {
75      write!(f, "{}{}", self.value, self.unit)
76    }
77  }
78}
79
80#[cfg(test)]
81mod tests {
82  use super::*;
83
84  #[test]
85  fn test_time_creation() {
86    let time = Time::new(1.5, "s".to_string());
87    assert_eq!(time.value, 1.5);
88    assert_eq!(time.unit, "s");
89  }
90
91  #[test]
92  fn test_time_display() {
93    let seconds = Time::new(1.5, "s".to_string());
94    assert_eq!(seconds.to_string(), "1.5s");
95
96    let milliseconds = Time::new(500.0, "ms".to_string());
97    assert_eq!(milliseconds.to_string(), "0.5s");
98
99    let full_second = Time::new(1000.0, "ms".to_string());
100    assert_eq!(full_second.to_string(), "1s");
101
102    let two_seconds = Time::new(2000.0, "ms".to_string());
103    assert_eq!(two_seconds.to_string(), "2s");
104
105    let partial = Time::new(1500.0, "ms".to_string());
106    assert_eq!(partial.to_string(), "1.5s");
107  }
108
109  #[test]
110  fn test_valid_time_units() {
111    assert!(Time::is_valid_unit("s"));
112    assert!(Time::is_valid_unit("ms"));
113
114    // Invalid units
115    assert!(!Time::is_valid_unit("px"));
116    assert!(!Time::is_valid_unit("deg"));
117    assert!(!Time::is_valid_unit("Hz"));
118  }
119
120  #[test]
121  fn test_time_units_constant() {
122    let units = Time::units();
123    assert_eq!(units.len(), 2);
124    assert!(units.contains(&"s"));
125    assert!(units.contains(&"ms"));
126  }
127
128  #[test]
129  fn test_time_parser_creation() {
130    // Basic test that parser can be created
131    let _parser = Time::parser();
132  }
133}