Skip to main content

azul_css/props/basic/
time.rs

1//! CSS property types for time durations (s, ms).
2
3use alloc::string::{String, ToString};
4use crate::corety::AzString;
5
6use crate::props::formatter::PrintAsCssValue;
7
8/// A CSS time duration, stored internally in milliseconds.
9#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[repr(C)]
11#[derive(Default)]
12pub struct CssDuration {
13    /// Duration in milliseconds.
14    pub inner: u32,
15}
16
17
18impl PrintAsCssValue for CssDuration {
19    fn print_as_css_value(&self) -> String {
20        format!("{}ms", self.inner)
21    }
22}
23
24impl crate::format_rust_code::FormatAsRustCode for CssDuration {
25    fn format_as_rust_code(&self, _tabs: usize) -> String {
26        format!("CssDuration {{ inner: {} }}", self.inner)
27    }
28}
29
30/// Error returned when parsing a CSS duration string fails.
31#[cfg(feature = "parser")]
32#[derive(Clone, PartialEq)]
33pub enum DurationParseError<'a> {
34    InvalidValue(&'a str),
35    ParseFloat(core::num::ParseFloatError),
36}
37
38#[cfg(feature = "parser")]
39impl_debug_as_display!(DurationParseError<'a>);
40#[cfg(feature = "parser")]
41impl_display! { DurationParseError<'a>, {
42    InvalidValue(v) => format!("Invalid time value: \"{}\"", v),
43    ParseFloat(e) => format!("Invalid number for time value: {}", e),
44}}
45
46/// Owned version of [`DurationParseError`] for FFI and storage.
47#[cfg(feature = "parser")]
48#[derive(Debug, Clone, PartialEq)]
49#[repr(C, u8)]
50pub enum DurationParseErrorOwned {
51    InvalidValue(AzString),
52    ParseFloat(AzString),
53}
54
55#[cfg(feature = "parser")]
56impl<'a> DurationParseError<'a> {
57    pub fn to_contained(&self) -> DurationParseErrorOwned {
58        match self {
59            Self::InvalidValue(s) => DurationParseErrorOwned::InvalidValue(s.to_string().into()),
60            Self::ParseFloat(e) => DurationParseErrorOwned::ParseFloat(e.to_string().into()),
61        }
62    }
63}
64
65#[cfg(feature = "parser")]
66impl DurationParseErrorOwned {
67    pub fn to_shared<'a>(&'a self) -> DurationParseError<'a> {
68        match self {
69            Self::InvalidValue(s) => DurationParseError::InvalidValue(s),
70            Self::ParseFloat(s) => DurationParseError::InvalidValue(s.as_str()),
71        }
72    }
73}
74
75/// Parses a CSS duration string (e.g. `"200ms"`, `"1.5s"`) into a [`CssDuration`].
76#[cfg(feature = "parser")]
77pub fn parse_duration<'a>(input: &'a str) -> Result<CssDuration, DurationParseError<'a>> {
78    let trimmed = input.trim().to_lowercase();
79    if trimmed == "0" {
80        return Ok(CssDuration { inner: 0 });
81    }
82    if let Some(num_str) = trimmed.strip_suffix("ms") {
83        let ms = num_str
84            .parse::<f32>()
85            .map_err(DurationParseError::ParseFloat)?;
86        if ms < 0.0 {
87            return Err(DurationParseError::InvalidValue(input));
88        }
89        Ok(CssDuration { inner: ms as u32 })
90    } else if let Some(num_str) = trimmed.strip_suffix('s') {
91        let s = num_str
92            .parse::<f32>()
93            .map_err(DurationParseError::ParseFloat)?;
94        if s < 0.0 {
95            return Err(DurationParseError::InvalidValue(input));
96        }
97        Ok(CssDuration {
98            inner: (s * 1000.0) as u32,
99        })
100    } else {
101        Err(DurationParseError::InvalidValue(input))
102    }
103}