1use std::{fmt, io};
2use std::io::Write as _;
3use std::fmt::Write as _;
4use crate::util::base64;
5use super::decode::Name;
6
7
8#[derive(Debug)]
12pub struct Writer<W> {
13 wrapped: W,
15
16 error: Option<io::Error>,
21
22 indent: &'static str,
24
25 indent_level: usize,
30}
31
32impl<W: io::Write> Writer<W> {
33 pub fn new(wrapped: W) -> Self {
37 Writer {
38 wrapped,
39 error: None,
40 indent: " ",
41 indent_level: 0,
42 }
43 }
44
45 pub fn set_indent(&mut self, s: &'static str) {
50 self.indent = s
51 }
52
53 pub fn element<'s>(
61 &'s mut self, tag: Name<'static, 'static>,
62 ) -> Result<Element<'s, W>, io::Error> {
63 Element::start(self, tag)
64 }
65
66 pub fn into_wrapped(mut self) -> Result<W, io::Error> {
68 if let Some(err) = self.error.take() {
69 Err(err)
70 }
71 else {
72 Ok(self.wrapped)
73 }
74 }
75
76 pub fn done(mut self) -> Result<(), io::Error> {
78 if let Some(err) = self.error.take() {
79 Err(err)
80 }
81 else {
82 Ok(())
83 }
84 }
85}
86
87impl<W: io::Write> Writer<W> {
90 fn store_error(&mut self, error: io::Error) {
92 self.error = Some(error)
93 }
94
95 fn indent(&mut self) {
97 self.indent_level = self.indent_level.saturating_add(1);
98 }
99
100 fn dedent(&mut self) {
102 self.indent_level = self.indent_level.saturating_sub(1);
103 }
104
105 fn write_indent(&mut self) -> Result<(), io::Error> {
109 if self.indent_level == 0 || self.indent.is_empty() {
110 return Ok(())
111 }
112
113 for _ in 0..self.indent_level {
114 self.write_all(self.indent.as_bytes())?
115 }
116 Ok(())
117 }
118}
119
120impl<W: io::Write> io::Write for Writer<W> {
121 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
122 if let Some(err) = self.error.take() {
123 return Err(err)
124 }
125 self.wrapped.write(buf)
126 }
127
128 fn flush(&mut self) -> Result<(), io::Error> {
129 if let Some(err) = self.error.take() {
130 return Err(err)
131 }
132 self.wrapped.flush()
133 }
134}
135
136
137#[derive(Debug)]
141pub struct Element<'a, W: io::Write> {
142 writer: &'a mut Writer<W>,
144
145 tag: Name<'static, 'static>,
147
148 empty: bool,
153}
154
155impl<'a, W: io::Write> Element<'a, W> {
156 fn start(
161 writer: &'a mut Writer<W>, tag: Name<'static, 'static>,
162 ) -> Result<Self, io::Error> {
163 writer.write_all(b"<")?;
164 if let Some(ns) = tag.namespace() {
165 writer.write_all(ns)?;
166 writer.write_all(b":")?;
167 }
168 writer.write_all(tag.local())?;
169 Ok(Element { writer, tag, empty: true })
170 }
171
172 pub fn attr(
174 mut self, name: &str, value: &(impl Text + ?Sized),
175 ) -> Result<Self, io::Error> {
176 self.writer.write_all(b" ")?;
177 self.writer.write_all(name.as_bytes())?;
178 self.writer.write_all(b"=\"")?;
179 value.write_escaped(TextEscape::Attr, &mut self.writer)?;
180 self.writer.write_all(b"\"")?;
181 Ok(self)
182 }
183
184 pub fn attr_opt(
186 self, name: &str, value: Option<&(impl Text + ?Sized)>,
187 ) -> Result<Self, io::Error> {
188 match value {
189 None => Ok(self),
190 Some(value) => self.attr(name, value)
191 }
192 }
193
194 pub fn content(
198 mut self, op: impl FnOnce(&mut Content<W>) -> Result<(), io::Error>
199 ) -> Result<Self, io::Error> {
200 self.empty = false;
201 self.writer.write_all(b">")?;
202 self.writer.indent();
203 op(&mut Content { writer: self.writer})?;
204 self.writer.dedent();
205 Ok(self)
206 }
207
208 fn end(&mut self) -> Result<(), io::Error> {
210 if self.empty {
211 self.writer.write_all(b"/>")?;
212 }
213 else {
214 self.writer.write_all(b"\n")?;
215 self.writer.write_indent()?;
216 self.writer.write_all(b"</")?;
217 if let Some(ns) = self.tag.namespace() {
218 self.writer.write_all(ns)?;
219 self.writer.write_all(b":")?;
220 }
221 self.writer.write_all(self.tag.local())?;
222 self.writer.write_all(b">")?;
223 }
224 Ok(())
225 }
226}
227
228impl<W: io::Write> Drop for Element<'_, W> {
229 fn drop(&mut self) {
230 if let Err(err) = self.end() {
231 self.writer.store_error(err)
232 }
233 }
234}
235
236
237#[derive(Debug)]
244pub struct Content<'a, W> {
245 writer: &'a mut Writer<W>,
247}
248
249impl<W: io::Write> Content<'_, W> {
250 pub fn element<'s>(
258 &'s mut self, tag: Name<'static, 'static>
259 ) -> Result<Element<'s, W>, io::Error> {
260 self.writer.write_all(b"\n")?;
261 self.writer.write_indent()?;
262 Element::start(self.writer, tag)
263 }
264
265 pub fn element_opt<'s, T>(
268 &'s mut self,
269 option: Option<&T>,
270 tag: Name<'static, 'static>,
271 op: impl FnOnce(&T, Element<'s, W>) -> Result<(), io::Error>
272 ) -> Result<(), io::Error> {
273 if let Some(opt) = option {
274 let element = self.element(tag)?;
275 op(opt, element)
276 } else {
277 Ok(())
278 }
279 }
280
281 pub fn pcdata(
285 &mut self, text: &(impl Text + ?Sized)
286 ) -> Result<(), io::Error> {
287 self.writer.write_all(b"\n")?;
288 self.writer.write_indent()?;
289 text.write_escaped(TextEscape::Pcdata, &mut self.writer)
290 }
291
292 pub fn raw(
296 &mut self, text: &(impl Text + ?Sized)
297 ) -> Result<(), io::Error> {
298 self.writer.write_all(b"\n")?;
299 self.writer.write_indent()?;
300 text.write_raw(&mut self.writer)
301 }
302
303 pub fn base64(
305 &mut self, data: &(impl Text + ?Sized)
306 ) -> Result<(), io::Error> {
307 self.writer.write_all(b"\n")?;
308 self.writer.write_indent()?;
309 data.write_base64(&mut self.writer)
310 }
311}
312
313
314pub trait Text {
321 fn write_escaped(
323 &self, mode: TextEscape, target: &mut impl io::Write
324 ) -> Result<(), io::Error>;
325
326 fn write_raw(
328 &self, target: &mut impl io::Write
329 ) -> Result<(), io::Error>;
330
331 fn write_base64(
333 &self, target: &mut impl io::Write
334 ) -> Result<(), io::Error> {
335 self.write_raw(&mut base64::Xml.encode_writer(target))
336 }
337}
338
339impl Text for [u8] {
340 fn write_escaped(
341 &self, mode: TextEscape, target: &mut impl io::Write
342 ) -> Result<(), io::Error> {
343 mode.write_escaped(self, target)
344 }
345
346 fn write_raw(
347 &self, target: &mut impl io::Write
348 ) -> Result<(), io::Error> {
349 target.write_all(self)
350 }
351}
352
353impl Text for str {
354 fn write_escaped(
355 &self, mode: TextEscape, target: &mut impl io::Write
356 ) -> Result<(), io::Error> {
357 mode.write_escaped(self.as_bytes(), target)
358 }
359
360 fn write_raw(
361 &self, target: &mut impl io::Write
362 ) -> Result<(), io::Error> {
363 target.write_all(self.as_bytes())
364 }
365}
366
367impl<T: fmt::Display> Text for T {
368 fn write_escaped(
369 &self, mode: TextEscape, target: &mut impl io::Write
370 ) -> Result<(), io::Error> {
371 let mut adaptor = DisplayText::new(target, mode);
372 match write!(adaptor, "{self}") {
373 Ok(()) => Ok(()),
374 Err(_) => match adaptor.into_result() {
375 Ok(()) => Err(io::Error::other("formatter error")),
376 Err(err) => Err(err)
377 }
378 }
379 }
380
381 fn write_raw(
382 &self, target: &mut impl io::Write
383 ) -> Result<(), io::Error> {
384 write!(target, "{self}")
385 }
386}
387
388
389struct DisplayText<'a, W> {
393 inner: &'a mut W,
394 escape: TextEscape,
395 error: Result<(), io::Error>,
396}
397
398impl<'a, W: io::Write> DisplayText<'a, W> {
399 fn new(inner: &'a mut W, escape: TextEscape) -> Self {
401 DisplayText {
402 inner, escape,
403 error: Ok(()),
404 }
405 }
406
407 fn into_result(self) -> Result<(), io::Error> {
412 self.error
413 }
414}
415
416impl<W: io::Write> fmt::Write for DisplayText<'_, W> {
417 fn write_str(&mut self, s: &str) -> fmt::Result {
418 match self.escape.write_escaped(s.as_bytes(), self.inner) {
419 Ok(()) => Ok(()),
420 Err(err) => {
421 self.error = Err(err);
422 Err(fmt::Error)
423 }
424 }
425 }
426}
427
428
429#[derive(Clone, Copy, Debug)]
433pub enum TextEscape {
434 Attr,
436
437 Pcdata,
439}
440
441impl TextEscape {
442 fn replace_char(self, ch: u8) -> Option<&'static str> {
444 match self {
445 TextEscape::Attr => {
446 match ch {
447 b'<' => Some("<"),
448 b'>' => Some(">"),
449 b'"' => Some("""),
450 b'\'' => Some("'"),
451 b'&' => Some("&"),
452 _ => None
453 }
454 }
455 TextEscape::Pcdata => {
456 match ch {
457 b'<' => Some("<"),
458 b'&' => Some("&"),
459 _ => None
460 }
461 }
462 }
463 }
464
465 fn write_escaped(
467 self, mut s: &[u8], target: &mut impl io::Write
468 ) -> Result<(), io::Error> {
469 while !s.is_empty() {
470 let mut iter = s.iter().enumerate().map(|(idx, ch)| {
471 (idx, self.replace_char(*ch))
472 });
473 let end = loop {
474 match iter.next() {
475 Some((idx, Some(repl))) => {
476 target.write_all(&s[0..idx])?;
479 target.write_all(repl.as_bytes())?;
480 break idx;
481 }
482 Some((_, None)) => { }
483 None => {
484 return target.write_all(s);
485 }
486 }
487 };
488 s = &s[end + 1..];
489 }
490 Ok(())
491 }
492}
493
494#[cfg(test)]
497mod test {
498 use super::*;
499
500 const ROOT_TAG: Name = Name::unqualified(b"root");
501 const OUTER_TAG: Name = Name::unqualified(b"outer");
502 const INNER_TAG: Name = Name::unqualified(b"inner");
503
504 #[test]
505 fn xml_doc_should_not_be_wrapped_with_whitespace() -> io::Result<()> {
506 let mut buf = Vec::<u8>::new();
507 let mut writer = Writer::new(&mut buf);
508
509 writer.element(ROOT_TAG)?;
510 writer.done()?;
511
512 assert_eq!("<root/>", std::str::from_utf8(&buf).unwrap());
513 Ok(())
514 }
515
516 #[test]
517 fn test_indent() -> io::Result<()> {
518 let mut buf = Vec::<u8>::new();
519 let mut writer = Writer::new(&mut buf);
520
521 writer
522 .element(ROOT_TAG)?
523 .content(|content| {
524 content.element(OUTER_TAG)?.content(|content| {
525 content.element(INNER_TAG)?;
526 Ok(())
527 })?;
528 Ok(())
529 })?;
530 writer.done()?;
531
532 assert_eq!(
533 "<root>\
534 \n <outer>\
535 \n <inner/>\
536 \n </outer>\
537 \n</root>",
538 std::str::from_utf8(&buf).unwrap()
539 );
540 Ok(())
541 }
542}