1use std::fmt::Write;
2
3use crate::{AttributeName, AttributeValue, escapes::StringEscaper, Attributes};
4
5struct TagOpeningData<'a, 't, W: Write> {
6 tag: &'t str,
7 w: &'a mut W,
8 compactability: Compactability,
9}
10
11
12pub struct TagOpening<'a, 't, W: Write> {
14 data: Option<TagOpeningData<'a, 't, W>>,
15}
16
17impl<'a, 't, W: Write> TagOpening<'a, 't, W> {
18 pub fn new(tag: &'t str, w: &'a mut W, compactability: Compactability) -> Result<Self, std::fmt::Error> {
20 w.write_str("<")?;
21 w.write_str(tag)?;
22 Ok(Self { data: Some(TagOpeningData { tag, w, compactability }) })
23 }
24
25 pub fn attr<'s>(
31 &'s mut self,
32 name: impl AttributeName,
33 value: impl AttributeValue
34 ) -> Result<&'s mut Self, std::fmt::Error> {
35 let data = self.data.as_mut().unwrap();
36 data.w.write_str(" ").unwrap();
37
38 assert!(name.is_valid_attribute_name());
39 name.write_attribute_name(data.w)?;
40
41 if value.is_unit() {
42 return Ok(self);
43 } else {
44 data.w.write_str("=\"")?;
45 value.write_attribute_value(&mut StringEscaper::new(data.w))?;
46 data.w.write_str("\"")?;
47 }
48
49 Ok(self)
50 }
51
52 pub fn with_attr(mut self, name: impl AttributeName, value: impl AttributeValue) -> Result<Self, std::fmt::Error> {
56 self.attr(name, value)?;
57
58 Ok(self)
59 }
60
61 pub fn with_attributes(mut self, attributes: impl Attributes) -> Result<Self, std::fmt::Error> {
63 attributes.write_attributes(&mut self)?;
64 Ok(self)
65 }
66
67 pub fn inner_html(mut self) -> Result<InsideTagHtml<'a, 't, W>, std::fmt::Error> {
69 let data = self.data.take().unwrap();
71 data.w.write_str(">")?;
72 Ok(InsideTagHtml { tag: data.tag, w: data.w })
73 }
74}
75
76impl<'a, 't, W: Write> Drop for TagOpening<'a, 't, W> {
77 fn drop(&mut self) {
78 if let Some(data) = self.data.take() {
79 if let Compactability::Yes { final_slash } = data.compactability {
80 let _ = data.w.write_str(if final_slash { "/>" } else { ">" });
81 } else {
82 let _ = data.w.write_fmt(format_args!("></{}>", data.tag));
83 }
84 }
85 }
86}
87
88pub struct InsideTagHtml<'a, 't, W: Write> {
90 tag: &'t str,
91 w: &'a mut W,
92}
93
94impl<'a, 't, W: Write> InsideTagHtml<'a, 't, W> {
95 }
97
98impl<'a, 't, W: Write> Drop for InsideTagHtml<'a, 't, W> {
99 fn drop(&mut self) {
100 let _ = self.w.write_str("</");
101 let _ = self.w.write_str(self.tag);
102 let _ = self.w.write_str(">");
103 }
104}
105
106impl<'a, 't, W: Write> Write for InsideTagHtml<'a, 't, W> {
107 fn write_str(&mut self, s: &str) -> std::fmt::Result {
108 self.w.write_str(s)
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
114pub enum Compactability {
115 No,
119
120 Yes {
126 final_slash: bool
128 },
129}
130
131impl Compactability {
132 pub fn is_compactable(&self) -> bool {
134 match self {
135 Compactability::No => false,
136 Compactability::Yes { .. } => true,
137 }
138 }
139}
140
141impl From<bool> for Compactability {
142 fn from(b: bool) -> Self {
143 if b {
144 Compactability::Yes { final_slash: true }
145 } else {
146 Compactability::No
147 }
148 }
149}
150
151impl From<Compactability> for bool {
152 fn from(c: Compactability) -> Self {
153 match c {
154 Compactability::No => false,
155 Compactability::Yes { .. } => true,
156 }
157 }
158}