use crate::element::{Element, ElementData};
use crate::error::{Error, Result};
use crate::parser::{DocumentParser, ReadOptions};
use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
use quick_xml::Writer;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::str::FromStr;
#[derive(Debug)]
pub enum Node {
Element(Element),
Text(String),
Comment(String),
CData(String),
PI(String),
DocType(String),
}
impl Node {
pub fn as_element(&self) -> Option<Element> {
match self {
Self::Element(elem) => Some(*elem),
_ => None,
}
}
pub(crate) fn build_text_content<'a>(&self, doc: &'a Document, buf: &'a mut String) {
match self {
Node::Element(elem) => elem.build_text_content(doc, buf),
Node::Text(text) => buf.push_str(text),
Node::CData(text) => buf.push_str(text),
Node::PI(text) => buf.push_str(text),
_ => {}
}
}
pub fn text_content(&self, doc: &Document) -> String {
let mut buf = String::new();
self.build_text_content(doc, &mut buf);
buf
}
}
#[derive(Debug)]
pub struct Document {
pub(crate) counter: usize, pub(crate) store: Vec<ElementData>,
container: Element,
pub(crate) version: String,
pub(crate) standalone: bool,
}
impl Document {
pub fn new() -> Document {
let (container, container_data) = Element::container();
Document {
counter: 1, store: vec![container_data],
container,
version: String::from("1.0"),
standalone: false,
}
}
pub fn container(&self) -> Element {
self.container
}
pub fn is_empty(&self) -> bool {
self.store.len() == 1
}
pub fn root_nodes(&self) -> &Vec<Node> {
self.container.children(self)
}
pub fn root_element(&self) -> Option<Element> {
self.container.child_elements(self).get(0).copied()
}
pub fn push_root_node(&mut self, node: Node) -> Result<()> {
let elem = self.container;
elem.push_child(self, node)
}
}
impl Document {
pub fn parse_str(str: &str) -> Result<Document> {
DocumentParser::parse_reader(str.as_bytes(), ReadOptions::default())
}
pub fn parse_str_with_opts(str: &str, opts: ReadOptions) -> Result<Document> {
DocumentParser::parse_reader(str.as_bytes(), opts)
}
pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<Document> {
let file = File::open(path)?;
DocumentParser::parse_reader(file, ReadOptions::default())
}
pub fn parse_file_with_opts<P: AsRef<Path>>(path: P, opts: ReadOptions) -> Result<Document> {
let file = File::open(path)?;
DocumentParser::parse_reader(file, opts)
}
pub fn parse_reader<R: Read>(reader: R) -> Result<Document> {
DocumentParser::parse_reader(reader, ReadOptions::default())
}
pub fn parse_reader_with_opts<R: Read>(reader: R, opts: ReadOptions) -> Result<Document> {
DocumentParser::parse_reader(reader, opts)
}
}
pub struct WriteOptions {
pub indent_char: u8,
pub indent_size: usize,
pub write_decl: bool,
}
impl WriteOptions {
pub fn default() -> WriteOptions {
WriteOptions {
indent_char: b' ',
indent_size: 2,
write_decl: true,
}
}
}
impl Document {
pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
self.write_file_with_opts(path, WriteOptions::default())
}
pub fn write_file_with_opts<P: AsRef<Path>>(&self, path: P, opts: WriteOptions) -> Result<()> {
let mut file = File::open(path)?;
self.write_with_opts(&mut file, opts)
}
pub fn write_str(&self) -> Result<String> {
self.write_str_with_opts(WriteOptions::default())
}
pub fn write_str_with_opts(&self, opts: WriteOptions) -> Result<String> {
let mut buf: Vec<u8> = Vec::with_capacity(200);
self.write_with_opts(&mut buf, opts)?;
Ok(String::from_utf8(buf)?)
}
pub fn write(&self, writer: &mut impl Write) -> Result<()> {
self.write_with_opts(writer, WriteOptions::default())
}
pub fn write_with_opts(&self, writer: &mut impl Write, opts: WriteOptions) -> Result<()> {
let container = self.container();
let mut writer = Writer::new_with_indent(writer, opts.indent_char, opts.indent_size);
if opts.write_decl {
self.write_decl(&mut writer)?;
}
self.write_nodes(&mut writer, container.children(self))?;
writer.write_event(Event::Eof)?;
Ok(())
}
fn write_decl(&self, writer: &mut Writer<impl Write>) -> Result<()> {
let standalone = match self.standalone {
true => Some("yes".as_bytes()),
false => None,
};
writer.write_event(Event::Decl(BytesDecl::new(
self.version.as_bytes(),
Some("UTF-8".as_bytes()),
standalone,
)))?;
Ok(())
}
fn write_nodes(&self, writer: &mut Writer<impl Write>, nodes: &[Node]) -> Result<()> {
for node in nodes {
match node {
Node::Element(eid) => self.write_element(writer, *eid)?,
Node::Text(text) => {
writer.write_event(Event::Text(BytesText::from_plain_str(text)))?
}
Node::DocType(text) => writer.write_event(Event::DocType(
BytesText::from_plain_str(&format!(" {}", text)), ))?,
Node::Comment(text) => {
writer.write_event(Event::Comment(BytesText::from_escaped_str(text)))?
}
Node::CData(text) => {
writer.write_event(Event::CData(BytesText::from_escaped_str(text)))?
}
Node::PI(text) => {
writer.write_event(Event::PI(BytesText::from_escaped_str(text)))?
}
};
}
Ok(())
}
fn write_element(&self, writer: &mut Writer<impl Write>, element: Element) -> Result<()> {
let name_bytes = element.full_name(self).as_bytes();
let mut start = BytesStart::borrowed_name(name_bytes);
for (key, val) in element.attributes(self) {
let val = quick_xml::escape::escape(val.as_bytes());
start.push_attribute((key.as_bytes(), &val[..]));
}
for (prefix, val) in element.namespace_decls(self) {
let attr_name = if prefix.is_empty() {
"xmlns".to_string()
} else {
format!("xmlns:{}", prefix)
};
let val = quick_xml::escape::escape(val.as_bytes());
start.push_attribute((attr_name.as_bytes(), &val[..]));
}
if element.has_children(self) {
writer.write_event(Event::Start(start))?;
self.write_nodes(writer, element.children(self))?;
writer.write_event(Event::End(BytesEnd::borrowed(name_bytes)))?;
} else {
writer.write_event(Event::Empty(start))?;
}
Ok(())
}
}
impl FromStr for Document {
type Err = Error;
fn from_str(s: &str) -> Result<Document> {
Document::parse_str(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_element() {
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<basic>
Text
<c />
</basic>
"#;
let mut doc = Document::from_str(xml).unwrap();
let basic = doc.container().children(&doc)[0].as_element().unwrap();
let p = Element::new(&mut doc, "p");
basic.push_child(&mut doc, Node::Element(p)).unwrap();
assert_eq!(p.parent(&doc).unwrap(), basic);
assert_eq!(
p,
basic.children(&doc).last().unwrap().as_element().unwrap()
)
}
}