silkenweb/
attribute.rs

1//! Traits for defining attribute types.
2//!
3//! [`Attribute`] defines how an attribute is rendered and [`AsAttribute`] is a
4//! marker trait to say where the attribute can be used.
5//!
6//! For example, both `String` and `&str` can be used as `String` attributes
7//! because `&str` implements `AsAttribute<String>`.
8//!
9//! Once you've implemented [`Attribute`] and [`AsAttribute`] for your type, you
10//! can use it with [`Element::attribute`], or define attributes on your
11//! own html element using the [`custom_html_element`] macro.
12//!
13//! [`Element::attribute`]: crate::node::element::Element::attribute
14
15/// A type that can be used as the value of an attribute.
16///
17/// See [module-level documentation](self) for more details.
18pub trait Attribute {
19    type Text<'a>: 'a + AsRef<str> + Into<String> + ToString
20    where
21        Self: 'a;
22
23    /// The attribute value text.
24    ///
25    /// Return `Some(text)` to set the attribute, or `None` to unset the
26    /// attribute. For example, `bool` attributes set the attribute if `true`,
27    /// or unset the attribute if `false`.
28    fn text(&self) -> Option<Self::Text<'_>>;
29}
30
31/// Define where an attribute type can be used.
32///
33/// See [module-level documentation](self) for more details.
34pub trait AsAttribute<T>: Attribute {}
35
36macro_rules! define_attribute_values{
37    ($($typ:ty),* $(,)?) => {
38        $(
39            impl Attribute for $typ {
40                type Text<'a> = String;
41
42                fn text(&self) -> Option<Self::Text<'_>> {
43                    Some(self.to_string())
44                }
45            }
46
47            impl AsAttribute<$typ> for $typ {}
48        )*
49    }
50}
51
52define_attribute_values!(i8, i16, i32, i64);
53define_attribute_values!(u8, u16, u32, u64);
54define_attribute_values!(f32, f64);
55define_attribute_values!(usize);
56
57impl Attribute for String {
58    type Text<'a> = &'a str;
59
60    fn text(&self) -> Option<Self::Text<'_>> {
61        Some(self)
62    }
63}
64
65impl AsAttribute<String> for String {}
66
67impl<T: Attribute> Attribute for Option<T> {
68    type Text<'a>
69        = T::Text<'a>
70    where
71        T: 'a;
72
73    fn text(&self) -> Option<Self::Text<'_>> {
74        self.as_ref()?.text()
75    }
76}
77
78impl<U: Attribute, T: AsAttribute<U>> AsAttribute<U> for Option<T> {}
79
80impl Attribute for bool {
81    type Text<'a> = &'static str;
82
83    fn text(&self) -> Option<Self::Text<'_>> {
84        if *self {
85            Some("")
86        } else {
87            None
88        }
89    }
90}
91
92impl AsAttribute<bool> for bool {}
93
94impl<'a> Attribute for &'a str {
95    type Text<'b>
96        = &'b str
97    where
98        'a: 'b;
99
100    fn text(&self) -> Option<Self::Text<'_>> {
101        Some(*self)
102    }
103}
104
105impl AsAttribute<String> for &str {}
106
107impl<'a> Attribute for &'a String {
108    type Text<'b>
109        = &'b str
110    where
111        'a: 'b;
112
113    fn text(&self) -> Option<Self::Text<'_>> {
114        Some(self.as_str())
115    }
116}
117
118impl AsAttribute<String> for &String {}