1use crate::Render;
2use std::fmt::{self, Write};
3
4pub trait Value: private::Sealed {
49 #[doc(hidden)]
50 fn render_value_to(&self, name: &str, f: &mut fmt::Formatter) -> fmt::Result;
51}
52
53impl Value for bool {
54 fn render_value_to(&self, name: &str, f: &mut fmt::Formatter) -> fmt::Result {
55 if *self { write!(f, " {name}") } else { Ok(()) }
56 }
57}
58
59impl<R: Render> Value for Option<R> {
60 fn render_value_to(&self, name: &str, f: &mut fmt::Formatter) -> fmt::Result {
61 if let Some(r) = self {
62 write!(f, " {name}=\"")?;
63 r.render_to(f)?;
64 f.write_char('"')?;
65 }
66
67 Ok(())
68 }
69}
70
71impl<R: Render> Value for R {
72 fn render_value_to(&self, name: &str, f: &mut fmt::Formatter) -> fmt::Result {
73 write!(f, " {name}=\"")?;
74 self.render_to(f)?;
75 f.write_char('"')?;
76
77 Ok(())
78 }
79}
80
81mod private {
82 pub trait Sealed {}
83
84 impl<T: crate::Value> Sealed for T {}
85}
86
87#[cfg(test)]
88mod tests {
89 use super::Value;
90 use std::fmt;
91
92 #[test]
93 fn bool_attributes() {
94 let display = fmt::from_fn(|f| {
95 false.render_value_to("checked", f).unwrap();
96 true.render_value_to("some-attribute", f)
97 });
98
99 assert_eq!(display.to_string(), " some-attribute");
100 }
101
102 #[test]
103 fn optional_attributes() {
104 let display = fmt::from_fn(|f| {
105 None::<i32>.render_value_to("hello-world", f).unwrap();
106 Some("escape this\"<").render_value_to("attr-123", f)
107 });
108
109 assert_eq!(display.to_string(), r#" attr-123="escape this"<""#);
110 }
111
112 #[test]
113 fn normal_attributes() {
114 let display = fmt::from_fn(|f| {
115 "&".render_value_to("hello-world", f).unwrap();
116 "escape this\"<".render_value_to("attr-123", f)
117 });
118
119 assert_eq!(
120 display.to_string(),
121 r#" hello-world="&" attr-123="escape this"<""#
122 );
123 }
124}