use std::{fmt::Write as FmtWrite, io::Write};
use xml::writer::{EmitterConfig, EventWriter};
pub use xml::writer::XmlEvent as XmlWriteEvent;
use crate::{
core::XmlType,
error::{EncodeError as NewEncodeError, EncodeErrorKind},
};
pub struct XmlEventWriter<W> {
inner: EventWriter<W>,
character_buffer: String,
}
impl<W: Write> XmlEventWriter<W> {
pub fn from_output(output: W) -> XmlEventWriter<W> {
let inner = EmitterConfig::new()
.perform_indent(true)
.write_document_declaration(false)
.normalize_empty_elements(false)
.create_writer(output);
XmlEventWriter {
inner,
character_buffer: String::new(),
}
}
pub(crate) fn error<T: Into<EncodeErrorKind>>(&self, kind: T) -> NewEncodeError {
NewEncodeError::new(kind.into())
}
pub fn end_element(&mut self) -> Result<(), NewEncodeError> {
self.inner
.write(XmlWriteEvent::end_element())
.map_err(|e| self.error(e))
}
pub fn write<'a, E>(&mut self, event: E) -> Result<(), NewEncodeError>
where
E: Into<XmlWriteEvent<'a>>,
{
self.inner.write(event).map_err(|e| self.error(e))
}
pub fn write_string(&mut self, value: &str) -> Result<(), NewEncodeError> {
write_characters_or_cdata(&mut self.inner, value)
}
pub fn write_characters<T: std::fmt::Display>(
&mut self,
value: T,
) -> Result<(), NewEncodeError> {
write!(self.character_buffer, "{value}").unwrap();
write_characters_or_cdata(&mut self.inner, &self.character_buffer)?;
self.character_buffer.clear();
Ok(())
}
pub fn write_value<T: XmlType>(&mut self, value: &T) -> Result<(), NewEncodeError> {
value.write_xml(self)
}
pub fn write_value_in_tag<T: XmlType>(
&mut self,
value: &T,
tag: &str,
) -> Result<(), NewEncodeError> {
self.write(XmlWriteEvent::start_element(tag))?;
self.write_value(value)?;
self.write(XmlWriteEvent::end_element())
}
pub fn write_tag_characters<T: std::fmt::Display>(
&mut self,
tag: &str,
value: T,
) -> Result<(), NewEncodeError> {
self.write(XmlWriteEvent::start_element(tag))?;
self.write_characters(value)?;
self.write(XmlWriteEvent::end_element())
}
pub fn write_tag_array<T: std::fmt::Display>(
&mut self,
values: &[T],
tags: &[&str],
) -> Result<(), NewEncodeError> {
assert_eq!(values.len(), tags.len());
for (index, component) in values.iter().enumerate() {
self.write_tag_characters(tags[index], component)?;
}
Ok(())
}
}
fn write_characters_or_cdata<W: Write>(
writer: &mut EventWriter<W>,
value: &str,
) -> Result<(), NewEncodeError> {
let first_char = value.chars().next();
let last_char = value.chars().next_back();
let has_outer_whitespace = match (first_char, last_char) {
(Some(first), Some(last)) => first.is_whitespace() || last.is_whitespace(),
(Some(char), None) | (None, Some(char)) => char.is_whitespace(),
(None, None) => false,
};
if has_outer_whitespace {
writer
.write(XmlWriteEvent::cdata(value))
.map_err(|e| NewEncodeError::new(e.into()))?;
} else {
writer
.write(XmlWriteEvent::characters(value))
.map_err(|e| NewEncodeError::new(e.into()))?;
}
Ok(())
}