to_text_xml: implement new function to convert Node and NodeCollection to text XML

This commit is contained in:
Matt Bilker
2018-10-29 15:52:52 +00:00
parent c24cbf895f
commit bc91024a40
4 changed files with 244 additions and 0 deletions

View File

@@ -30,12 +30,14 @@ mod reader;
mod sixbit;
mod text_reader;
mod to_element;
mod to_text_xml;
mod value;
mod writer;
use node::NodeDefinition;
use node_types::StandardType;
use text_reader::TextXmlReader;
use to_text_xml::TextXmlWriter;
// Public exports
pub use compression::Compression;
@@ -46,6 +48,7 @@ pub use error::{KbinError, KbinErrorKind, Result};
pub use node::{Node, NodeCollection};
pub use options::Options;
pub use to_element::ToElement;
pub use to_text_xml::ToTextXml;
pub use value::Value;
pub use writer::{Writer, Writeable};
@@ -197,3 +200,10 @@ pub fn to_binary_with_options<T>(options: Options, input: &T) -> Result<Vec<u8>>
let mut writer = Writer::with_options(options);
writer.to_binary(input)
}
pub fn to_text_xml<T>(input: &T) -> Result<Vec<u8>>
where T: ToTextXml
{
let writer = TextXmlWriter::new();
writer.to_text_xml(input)
}

35
src/to_text_xml/mod.rs Normal file
View File

@@ -0,0 +1,35 @@
use std::io::{Cursor, Write};
use quick_xml::Writer;
use error::KbinError;
mod node;
mod node_collection;
pub trait ToTextXml {
fn write<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), KbinError>;
}
pub struct TextXmlWriter {
xml_writer: Writer<Cursor<Vec<u8>>>,
}
impl TextXmlWriter {
pub fn new() -> Self {
let inner = Cursor::new(Vec::new());
let xml_writer = Writer::new_with_indent(inner, b' ', 2);
Self {
xml_writer,
}
}
pub fn to_text_xml<T>(mut self, value: &T) -> Result<Vec<u8>, KbinError>
where T: ToTextXml
{
value.write(&mut self.xml_writer)?;
Ok(self.xml_writer.into_inner().into_inner())
}
}

98
src/to_text_xml/node.rs Normal file
View File

@@ -0,0 +1,98 @@
use std::borrow::Cow;
use std::io::Write;
use quick_xml::Writer;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::events::attributes::Attribute;
use error::KbinError;
use node::Node;
use node_types::StandardType;
use to_text_xml::ToTextXml;
use value::Value;
impl ToTextXml for Node {
fn write<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), KbinError> {
let key = self.key();
let mut elem = BytesStart::borrowed(key.as_bytes(), key.as_bytes().len());
// Write the attributes for the value, but not the value contents.
if let Some(value) = self.value() {
let node_type = value.standard_type();
match value {
Value::Binary(ref data) => {
elem.push_attribute(Attribute {
key: b"__size",
value: Cow::Owned(data.len().to_string().into_bytes()),
});
},
Value::Array(_, ref values) => {
elem.push_attribute(Attribute {
key: b"__count",
value: Cow::Owned(values.len().to_string().into_bytes()),
});
},
_ => {},
};
// Only add a `__type` attribute if this is not a `NodeStart` node
if node_type != StandardType::NodeStart {
elem.push_attribute(Attribute {
key: b"__type",
value: Cow::Borrowed(node_type.name.as_bytes()),
});
}
}
if let Some(attributes) = self.attributes() {
for (key, value) in attributes {
elem.push_attribute(Attribute {
key: key.as_bytes(),
value: Cow::Borrowed(value.as_bytes()),
});
}
}
// Now write the value contents.
let start_elem = if let Some(value) = self.value() {
writer.write_event(Event::Start(elem))?;
let value = value.to_string();
let elem = BytesText::from_plain_str(&value);
writer.write_event(Event::Text(elem))?;
None
} else {
Some(elem)
};
let has_value = start_elem.is_none();
let has_children = match self.children() {
Some(children) => !children.is_empty(),
None => false,
};
// A `Some` value here means the start element was not written
if let Some(start_elem) = start_elem {
if !has_children {
writer.write_event(Event::Empty(start_elem))?;
} else {
writer.write_event(Event::Start(start_elem))?;
}
}
if let Some(children) = self.children() {
for child in children {
child.write(writer)?;
}
}
if has_value || has_children {
let end_elem = BytesEnd::borrowed(key.as_bytes());
writer.write_event(Event::End(end_elem))?;
}
Ok(())
}
}

View File

@@ -0,0 +1,101 @@
use std::borrow::Cow;
use std::io::Write;
use quick_xml::Writer;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::events::attributes::Attribute;
use error::{KbinError, KbinErrorKind};
use node::NodeCollection;
use node_types::StandardType;
use to_text_xml::ToTextXml;
impl ToTextXml for NodeCollection {
fn write<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), KbinError> {
let base = self.base();
let key = base.key()?.ok_or(KbinErrorKind::InvalidState)?;
let value = match base.value() {
Ok(value) => Some(value),
Err(e) => {
match e.get_context() {
KbinErrorKind::InvalidNodeType(_) => None,
_ => return Err(e),
}
},
};
let mut elem = BytesStart::borrowed(key.as_bytes(), key.as_bytes().len());
if base.is_array {
let values = value.as_ref().ok_or(KbinErrorKind::InvalidState)?.as_array()?;
elem.push_attribute(Attribute {
key: b"__count",
value: Cow::Owned(values.len().to_string().into_bytes()),
});
}
if base.node_type == StandardType::Binary {
let value = value.as_ref().ok_or(KbinErrorKind::InvalidState)?.as_slice()?;
elem.push_attribute(Attribute {
key: b"__size",
value: Cow::Owned(value.len().to_string().into_bytes()),
});
}
// Only add a `__type` attribute if this is not a `NodeStart` node
if base.node_type != StandardType::NodeStart {
elem.push_attribute(Attribute {
key: b"__type",
value: Cow::Borrowed(base.node_type.name.as_bytes()),
});
}
for attribute in self.attributes() {
let key = attribute.key()?.ok_or(KbinErrorKind::InvalidState)?.into_bytes();
let value = attribute.value()?.to_string().into_bytes();
elem.push_attribute(Attribute {
key: &key,
value: Cow::Owned(value),
});
}
let start_elem = match value {
Some(value) => {
writer.write_event(Event::Start(elem))?;
let value = value.to_string();
let elem = BytesText::from_plain_str(&value);
writer.write_event(Event::Text(elem))?;
None
},
None => Some(elem),
};
let has_value = start_elem.is_none();
let has_children = !self.children().is_empty();
// A `Some` value here means the start element was not written
if let Some(start_elem) = start_elem {
if !has_children {
writer.write_event(Event::Empty(start_elem))?;
} else {
writer.write_event(Event::Start(start_elem))?;
}
}
for child in self.children() {
child.write(writer)?;
}
if has_value || has_children {
let end_elem = BytesEnd::borrowed(key.as_bytes());
writer.write_event(Event::End(end_elem))?;
}
Ok(())
}
}