1use core::fmt;
4
5pub trait ToCss {
17 fn to_css(&self, dest: &mut dyn fmt::Write) -> fmt::Result;
19
20 fn to_css_string(&self) -> String {
22 let mut s = String::new();
23 let _ = self.to_css(&mut s);
25 s
26 }
27}
28
29impl<T: ToCss + ?Sized> ToCss for &T {
30 fn to_css(&self, dest: &mut dyn fmt::Write) -> fmt::Result {
31 (**self).to_css(dest)
32 }
33}
34
35pub(crate) fn write_number(dest: &mut dyn fmt::Write, n: f32) -> fmt::Result {
38 if n.fract() == 0.0 && n.is_finite() && n.abs() < 1e16 {
39 write!(dest, "{}", n as i64)
40 } else {
41 write!(dest, "{n}")
42 }
43}
44
45pub(crate) fn number_to_string(n: f32) -> String {
48 let mut s = String::new();
49 let _ = write_number(&mut s, n);
50 s
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 struct Token(&'static str);
60 impl ToCss for Token {
61 fn to_css(&self, dest: &mut dyn fmt::Write) -> fmt::Result {
62 dest.write_str(self.0)
63 }
64 }
65
66 #[test]
67 fn write_number_drops_decimal_for_integers() {
68 let mut buf = String::new();
69 write_number(&mut buf, 1.0).unwrap();
70 assert_eq!(buf, "1");
71 buf.clear();
72 write_number(&mut buf, -3.0).unwrap();
73 assert_eq!(buf, "-3");
74 buf.clear();
75 write_number(&mut buf, 0.0).unwrap();
76 assert_eq!(buf, "0");
77 }
78
79 #[test]
80 fn write_number_keeps_decimal_for_fractions() {
81 let mut buf = String::new();
82 write_number(&mut buf, 0.5).unwrap();
83 assert_eq!(buf, "0.5");
84 buf.clear();
85 write_number(&mut buf, -1.25).unwrap();
86 assert_eq!(buf, "-1.25");
87 }
88
89 #[test]
90 fn write_number_handles_non_finite_safely() {
91 let mut buf = String::new();
92 let _ = write_number(&mut buf, f32::NAN);
96 let _ = write_number(&mut buf, f32::INFINITY);
97 assert!(!buf.is_empty());
99 }
100
101 #[test]
102 fn write_number_handles_huge_floats() {
103 let mut buf = String::new();
104 write_number(&mut buf, 1e20).unwrap();
107 assert!(!buf.is_empty());
108 }
109
110 #[test]
111 fn number_to_string_matches_write_number() {
112 assert_eq!(number_to_string(1.0), "1");
113 assert_eq!(number_to_string(0.25), "0.25");
114 assert_eq!(number_to_string(-2.5), "-2.5");
115 }
116
117 #[test]
118 fn to_css_blanket_reference_impl() {
119 let t = Token("ident");
121 let r: &Token = &t;
122 let s = r.to_css_string();
123 assert_eq!(s, "ident");
124 }
125
126 #[test]
127 fn to_css_string_uses_the_same_path() {
128 let t = Token("abc");
129 assert_eq!(t.to_css_string(), "abc");
130 }
131}