1use std::fmt::Write;
2
3use crate::{TagOpening, Empty, Sum};
4
5
6pub trait Attributes {
8 fn write_attributes<'a, 't, W: Write>(self, w: &mut TagOpening<'a, 't, W>) -> std::fmt::Result;
13} impl<A: Attributes, B: Attributes> Attributes for Sum<A, B> {
16 fn write_attributes<'a, 't, W: Write>(self, w: &mut TagOpening<'a, 't, W>) -> std::fmt::Result {
17 self.0.write_attributes(w)?;
18 self.1.write_attributes(w)?;
19 Ok(())
20 }
21}
22
23impl<I: IntoIterator<Item = (Name, Value)>, Name, Value> Attributes for I
24where
25 Name: AttributeName,
26 Value: AttributeValue,
27{
28 fn write_attributes<'a, 't, W: Write>(self, w: &mut TagOpening<'a, 't, W>) -> std::fmt::Result {
29 for (n, v) in self {
30 w.attr(n, v)?;
31 }
32 Ok(())
33 }
34}
35
36impl Attributes for Empty {
37 fn write_attributes<'a, 't, W: Write>(self, _w: &mut TagOpening<'a, 't, W>) -> std::fmt::Result {
38 Ok(())
39 }
40}
41
42
43pub fn is_valid_attribute_name(name: &str) -> bool {
45 if name.is_empty() {
46 return false;
47 }
48
49 if name.trim() != name {
50 return false;
51 }
52
53 let mut chars = name.chars();
54 let first_char = chars.next().unwrap();
55
56 if !first_char.is_ascii_alphabetic() {
57 return false;
58 }
59
60 for c in chars {
61 if !c.is_ascii_alphanumeric() && c != '-' && c != '_' {
62 return false;
63 }
64 }
65
66 true
67}
68
69pub trait AttributeName {
71 fn is_valid_attribute_name(&self) -> bool;
73 fn write_attribute_name(self, w: &mut impl Write) -> std::fmt::Result;
78}
79
80impl AttributeName for &str {
81 fn is_valid_attribute_name(&self) -> bool {
82 is_valid_attribute_name(self)
83 }
84 fn write_attribute_name(self, w: &mut impl Write) -> std::fmt::Result {
85 w.write_str(self)
86 }
87}
88
89impl AttributeName for &&str {
90 fn is_valid_attribute_name(&self) -> bool {
91 is_valid_attribute_name(self)
92 }
93 fn write_attribute_name(self, w: &mut impl Write) -> std::fmt::Result {
94 w.write_str(*self)
95 }
96}
97
98impl AttributeName for String {
99 fn is_valid_attribute_name(&self) -> bool {
100 is_valid_attribute_name(&self)
101 }
102 fn write_attribute_name(self, w: &mut impl Write) -> std::fmt::Result {
103 w.write_str(&self)
104 }
105}
106
107impl AttributeName for &String {
108 fn is_valid_attribute_name(&self) -> bool {
109 is_valid_attribute_name(self)
110 }
111 fn write_attribute_name(self, w: &mut impl Write) -> std::fmt::Result {
112 w.write_str(self)
113 }
114}
115
116pub trait AttributeValue {
118 fn is_unit(&self) -> bool {
120 false
121 }
122 fn write_attribute_value(self, w: &mut impl Write) -> std::fmt::Result;
127}
128
129impl AttributeValue for &str {
130 fn is_unit(&self) -> bool {
131 false
132 }
133 fn write_attribute_value(self, w: &mut impl Write) -> std::fmt::Result {
134 w.write_str(self)
135 }
136}
137
138impl AttributeValue for &&str {
139 fn is_unit(&self) -> bool {
140 false
141 }
142 fn write_attribute_value(self, w: &mut impl Write) -> std::fmt::Result {
143 w.write_str(*self)
144 }
145}
146
147impl AttributeValue for String {
148 fn is_unit(&self) -> bool {
149 false
150 }
151 fn write_attribute_value(self, w: &mut impl Write) -> std::fmt::Result {
152 w.write_str(&self)
153 }
154}
155
156impl AttributeValue for &String {
157 fn is_unit(&self) -> bool {
158 false
159 }
160 fn write_attribute_value(self, w: &mut impl Write) -> std::fmt::Result {
161 w.write_str(self)
162 }
163}
164
165impl AttributeValue for () {
166 fn is_unit(&self) -> bool {
167 true
168 }
169 fn write_attribute_value(self, _w: &mut impl Write) -> std::fmt::Result {
170 Ok(())
171 }
172}
173
174