use crate::binxml::value_variant::BinXmlValue;
use crate::err::{SerializationError, SerializationResult};
use crate::model::xml::{BinXmlPI, XmlElement};
use crate::ParserSettings;
use log::trace;
use std::io::Write;
use quick_xml::events::attributes::Attribute;
use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
use quick_xml::Writer;
use crate::binxml::name::BinXmlName;
use std::borrow::Cow;
pub trait BinXmlOutput {
fn visit_end_of_stream(&mut self) -> SerializationResult<()>;
fn visit_open_start_element(
&mut self,
open_start_element: &XmlElement,
) -> SerializationResult<()>;
fn visit_close_element(&mut self, element: &XmlElement) -> SerializationResult<()>;
fn visit_characters(&mut self, value: Cow<BinXmlValue>) -> SerializationResult<()>;
fn visit_cdata_section(&mut self) -> SerializationResult<()>;
fn visit_entity_reference(&mut self, entity: &BinXmlName) -> SerializationResult<()>;
fn visit_character_reference(&mut self, char_ref: Cow<'_, str>) -> SerializationResult<()>;
fn visit_processing_instruction(&mut self, pi: &BinXmlPI) -> SerializationResult<()>;
fn visit_start_of_stream(&mut self) -> SerializationResult<()>;
}
pub struct XmlOutput<W: Write> {
writer: Writer<W>,
}
impl<W: Write> XmlOutput<W> {
pub fn with_writer(target: W, settings: &ParserSettings) -> Self {
let writer = if settings.should_indent() {
Writer::new_with_indent(target, b' ', 2)
} else {
Writer::new(target)
};
XmlOutput { writer }
}
pub fn into_writer(self) -> W {
self.writer.into_inner()
}
}
impl<W: Write> BinXmlOutput for XmlOutput<W> {
fn visit_end_of_stream(&mut self) -> SerializationResult<()> {
trace!("visit_end_of_stream");
self.writer.write_event(Event::Eof)?;
Ok(())
}
fn visit_open_start_element(&mut self, element: &XmlElement) -> SerializationResult<()> {
trace!("visit_open_start_element: {:?}", element);
let mut event_builder =
BytesStart::borrowed_name(element.name.as_ref().as_str().as_bytes());
for attr in element.attributes.iter() {
let value_cow: Cow<'_, str> = attr.value.as_ref().as_cow_str();
if value_cow.len() > 0 {
let name_as_str = attr.name.as_str();
let attr = Attribute::from((name_as_str, value_cow.as_ref()));
event_builder.push_attribute(attr);
}
}
self.writer.write_event(Event::Start(event_builder))?;
Ok(())
}
fn visit_close_element(&mut self, element: &XmlElement) -> SerializationResult<()> {
trace!("visit_close_element");
let event = BytesEnd::borrowed(element.name.as_ref().as_str().as_bytes());
self.writer.write_event(Event::End(event))?;
Ok(())
}
fn visit_characters(&mut self, value: Cow<BinXmlValue>) -> SerializationResult<()> {
trace!("visit_chars");
let cow: Cow<str> = value.as_cow_str();
let event = BytesText::from_plain_str(&cow);
self.writer.write_event(Event::Text(event))?;
Ok(())
}
fn visit_cdata_section(&mut self) -> SerializationResult<()> {
Err(SerializationError::Unimplemented {
message: format!("`{}`: visit_cdata_section", file!()),
})
}
fn visit_entity_reference(&mut self, entity: &BinXmlName) -> Result<(), SerializationError> {
let xml_ref = "&".to_string() + entity.as_str() + ";";
let event = Event::Text(BytesText::from_escaped_str(&xml_ref));
self.writer.write_event(event)?;
Ok(())
}
fn visit_character_reference(
&mut self,
_char_ref: Cow<'_, str>,
) -> Result<(), SerializationError> {
Err(SerializationError::Unimplemented {
message: format!("`{}`: visit_character_reference", file!()),
})
}
fn visit_processing_instruction(&mut self, pi: &BinXmlPI) -> SerializationResult<()> {
let concat = pi.name.as_str().to_owned() + pi.data.as_ref(); let event = Event::PI(BytesText::from_plain_str(concat.as_str()));
self.writer.write_event(event)?;
Ok(())
}
fn visit_start_of_stream(&mut self) -> SerializationResult<()> {
trace!("visit_start_of_stream");
let event = BytesDecl::new(b"1.0", Some(b"utf-8"), None);
self.writer.write_event(Event::Decl(event))?;
Ok(())
}
}