rsvg/
unit_interval.rs

1//! Type for values in the [0.0, 1.0] range.
2
3use cssparser::Parser;
4
5use crate::error::*;
6use crate::length::*;
7use crate::parsers::Parse;
8use crate::util;
9
10#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
11pub struct UnitInterval(pub f64);
12
13impl UnitInterval {
14    pub fn clamp(val: f64) -> UnitInterval {
15        UnitInterval(util::clamp(val, 0.0, 1.0))
16    }
17}
18
19impl Parse for UnitInterval {
20    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<UnitInterval, ParseError<'i>> {
21        let loc = parser.current_source_location();
22        let l: Length<Both> = Parse::parse(parser)?;
23        match l.unit {
24            LengthUnit::Px | LengthUnit::Percent => Ok(UnitInterval::clamp(l.length)),
25            _ => Err(loc.new_custom_error(ValueErrorKind::value_error(
26                "<unit-interval> must be in default or percent units",
27            ))),
28        }
29    }
30}
31
32impl From<UnitInterval> for u8 {
33    fn from(val: UnitInterval) -> u8 {
34        let UnitInterval(x) = val;
35        (x * 255.0 + 0.5).floor() as u8
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn clamps() {
45        assert_eq!(UnitInterval::clamp(-1.0), UnitInterval(0.0));
46        assert_eq!(UnitInterval::clamp(0.0), UnitInterval(0.0));
47        assert_eq!(UnitInterval::clamp(0.5), UnitInterval(0.5));
48        assert_eq!(UnitInterval::clamp(1.0), UnitInterval(1.0));
49        assert_eq!(UnitInterval::clamp(2.0), UnitInterval(1.0));
50    }
51
52    #[test]
53    fn parses_percentages() {
54        assert_eq!(UnitInterval::parse_str("-100%").unwrap(), UnitInterval(0.0));
55        assert_eq!(UnitInterval::parse_str("0%").unwrap(), UnitInterval(0.0));
56        assert_eq!(UnitInterval::parse_str("50%").unwrap(), UnitInterval(0.5));
57        assert_eq!(UnitInterval::parse_str("100%").unwrap(), UnitInterval(1.0));
58        assert_eq!(
59            UnitInterval::parse_str("100.1%").unwrap(),
60            UnitInterval(1.0)
61        );
62        assert_eq!(UnitInterval::parse_str("200%").unwrap(), UnitInterval(1.0));
63    }
64
65    #[test]
66    fn parses_number() {
67        assert_eq!(UnitInterval::parse_str("0").unwrap(), UnitInterval(0.0));
68        assert_eq!(UnitInterval::parse_str("1").unwrap(), UnitInterval(1.0));
69        assert_eq!(UnitInterval::parse_str("0.5").unwrap(), UnitInterval(0.5));
70    }
71
72    #[test]
73    fn parses_out_of_range_number() {
74        assert_eq!(UnitInterval::parse_str("-10").unwrap(), UnitInterval(0.0));
75        assert_eq!(UnitInterval::parse_str("10").unwrap(), UnitInterval(1.0));
76    }
77
78    #[test]
79    fn errors_on_invalid_input() {
80        assert!(UnitInterval::parse_str("").is_err());
81        assert!(UnitInterval::parse_str("foo").is_err());
82        assert!(UnitInterval::parse_str("-x").is_err());
83        assert!(UnitInterval::parse_str("0.0foo").is_err());
84        assert!(UnitInterval::parse_str("0.0%%").is_err());
85        assert!(UnitInterval::parse_str("%").is_err());
86    }
87
88    #[test]
89    fn convert() {
90        assert_eq!(u8::from(UnitInterval(0.0)), 0);
91        assert_eq!(u8::from(UnitInterval(0.5)), 128);
92        assert_eq!(u8::from(UnitInterval(1.0)), 255);
93    }
94}