1use 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}