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 XmlWriter<'_> {
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}