1use crate::escape::escape;
10use std::error::Error as StdError;
11use std::fmt::{self, Display, Formatter, Write};
12
13#[non_exhaustive]
15#[derive(Debug)]
16pub struct XmlEncodeError {}
17
18impl Display for XmlEncodeError {
19 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
20 write!(f, "error encoding XML")
21 }
22}
23
24impl StdError for XmlEncodeError {}
25
26pub struct XmlWriter<'a> {
54 doc: &'a mut String,
55}
56
57impl<'a> XmlWriter<'a> {
58 pub fn new(doc: &'a mut String) -> Self {
59 Self { doc }
60 }
61}
62
63impl<'a> XmlWriter<'a> {
64 pub fn start_el<'b, 'c>(&'c mut self, tag: &'b str) -> ElWriter<'c, 'b> {
65 write!(self.doc, "<{}", tag).unwrap();
66 ElWriter::new(self.doc, tag)
67 }
68}
69
70pub struct ElWriter<'a, 'b> {
71 start: &'b str,
72 doc: Option<&'a mut String>,
73}
74
75impl<'a, 'b> ElWriter<'a, 'b> {
76 fn new(doc: &'a mut String, start: &'b str) -> ElWriter<'a, 'b> {
77 ElWriter {
78 start,
79 doc: Some(doc),
80 }
81 }
82
83 pub fn write_attribute(&mut self, key: &str, value: &str) -> &mut Self {
84 write!(self.doc(), " {}=\"{}\"", key, escape(value)).unwrap();
85 self
86 }
87
88 pub fn write_ns(mut self, namespace: &str, prefix: Option<&str>) -> Self {
89 match prefix {
90 Some(prefix) => {
91 write!(self.doc(), " xmlns:{}=\"{}\"", prefix, escape(namespace)).unwrap()
92 }
93 None => write!(self.doc(), " xmlns=\"{}\"", escape(namespace)).unwrap(),
94 }
95 self
96 }
97
98 fn write_end(doc: &mut String) {
99 write!(doc, ">").unwrap();
100 }
101
102 fn doc<'c>(&'c mut self) -> &'c mut String
103 where
104 'a: 'c,
105 {
106 self.doc.as_mut().unwrap()
117 }
118
119 pub fn finish(mut self) -> ScopeWriter<'a, 'b> {
120 let doc = self.doc.take().unwrap();
121 Self::write_end(doc);
122 ScopeWriter {
123 doc,
124 start: self.start,
125 }
126 }
127}
128
129impl Drop for ElWriter<'_, '_> {
130 fn drop(&mut self) {
131 if let Some(doc) = self.doc.take() {
132 Self::write_end(doc);
136 }
137 }
138}
139
140pub struct ScopeWriter<'a, 'b> {
142 doc: &'a mut String,
143 start: &'b str,
144}
145
146impl Drop for ScopeWriter<'_, '_> {
147 fn drop(&mut self) {
148 write!(self.doc, "</{}>", self.start).unwrap();
149 }
150}
151
152impl ScopeWriter<'_, '_> {
153 pub fn data(&mut self, data: &str) {
154 self.doc.write_str(escape(data).as_ref()).unwrap();
155 }
156
157 pub fn finish(self) {
158 }
160
161 pub fn start_el<'b, 'c>(&'c mut self, tag: &'b str) -> ElWriter<'c, 'b> {
162 write!(self.doc, "<{}", tag).unwrap();
163 ElWriter::new(self.doc, tag)
164 }
165}
166
167#[cfg(test)]
168mod test {
169 use crate::encode::XmlWriter;
170 use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType};
171
172 #[test]
173 fn forgot_finish() {
174 let mut out = String::new();
175
176 fn writer(out: &mut String) {
177 let mut doc_writer = XmlWriter::new(out);
178 doc_writer.start_el("Hello");
179 }
182 writer(&mut out);
183
184 assert_ok(validate_body(out, r#"<Hello></Hello>"#, MediaType::Xml));
185 }
186
187 #[test]
188 fn forgot_finish_with_attribute() {
189 let mut out = String::new();
190
191 fn writer(out: &mut String) {
192 let mut doc_writer = XmlWriter::new(out);
193 doc_writer.start_el("Hello").write_attribute("key", "foo");
194 }
197 writer(&mut out);
198
199 assert_ok(validate_body(
200 out,
201 r#"<Hello key="foo"></Hello>"#,
202 MediaType::Xml,
203 ));
204 }
205
206 #[test]
207 fn basic_document_encoding() {
208 let mut out = String::new();
209 let mut doc_writer = XmlWriter::new(&mut out);
210 let mut start_el = doc_writer
211 .start_el("Hello")
212 .write_ns("http://example.com", None);
213 start_el.write_attribute("key", "foo");
214 let mut tag = start_el.finish();
215 let mut inner = tag.start_el("inner").finish();
216 inner.data("hello world!");
217 inner.finish();
218 let more_inner = tag.start_el("inner").finish();
219 more_inner.finish();
220 tag.finish();
221
222 assert_ok(validate_body(
223 out,
224 r#"<Hello key="foo" xmlns="http://example.com">
225 <inner>hello world!</inner>
226 <inner></inner>
227 </Hello>"#,
228 MediaType::Xml,
229 ));
230 }
231
232 #[test]
233 fn escape_data() {
234 let mut s = String::new();
235 {
236 let mut doc_writer = XmlWriter::new(&mut s);
237 let mut start_el = doc_writer.start_el("Hello");
238 start_el.write_attribute("key", "<key=\"value\">");
239 let mut tag = start_el.finish();
240 tag.data("\n\r&");
241 }
242 assert_eq!(
243 s,
244 r#"<Hello key="<key="value">">

&</Hello>"#
245 )
246 }
247}