Files

293 lines
8.1 KiB
Rust

use roxmltree::*;
use std::fmt;
use std::fmt::Write;
use std::fs;
use std::io::Read;
use std::path;
#[derive(Clone, Copy, PartialEq)]
struct TStr<'a>(pub &'a str);
impl<'a> fmt::Debug for TStr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
trait HasExtension {
fn has_extension(&self, ext: &str) -> bool;
}
impl HasExtension for path::Path {
fn has_extension(&self, ext: &str) -> bool {
if let Some(e) = self.extension() {
e == ext
} else {
false
}
}
}
fn actual_test(path: &str) {
let path = path::Path::new(path);
let expected = load_file(&path.with_extension("yaml"));
let opt = ParsingOptions {
allow_dtd: true,
..roxmltree::ParsingOptions::default()
};
let input_xml = load_file(path);
let doc = match Document::parse_with_options(&input_xml, opt) {
Ok(v) => v,
Err(e) => {
assert_eq!(TStr(&format!("error: \"{}\"", e)), TStr(expected.trim()));
return;
}
};
assert_eq!(TStr(&to_yaml(&doc)), TStr(&expected));
}
fn load_file(path: &path::Path) -> String {
let mut file = fs::File::open(path).unwrap();
let mut text = String::new();
file.read_to_string(&mut text).unwrap();
text
}
fn to_yaml(doc: &Document) -> String {
let mut s = String::new();
_to_yaml(doc, &mut s).unwrap();
s
}
fn _to_yaml(doc: &Document, s: &mut String) -> Result<(), fmt::Error> {
if !doc.root().has_children() {
return write!(s, "Document:");
}
macro_rules! writeln_indented {
($depth:expr, $f:expr, $fmt:expr) => {
for _ in 0..$depth { write!($f, " ")?; }
writeln!($f, $fmt)?;
};
($depth:expr, $f:expr, $fmt:expr, $($arg:tt)*) => {
for _ in 0..$depth { write!($f, " ")?; }
writeln!($f, $fmt, $($arg)*)?;
};
}
fn print_children(parent: Node, depth: usize, s: &mut String) -> Result<(), fmt::Error> {
for child in parent.children() {
match child.node_type() {
NodeType::Element => {
writeln_indented!(depth, s, "- Element:");
match child.tag_name().namespace() {
Some(ns) => {
if ns.is_empty() {
writeln_indented!(
depth + 2,
s,
"tag_name: {}",
child.tag_name().name()
);
} else {
writeln_indented!(
depth + 2,
s,
"tag_name: {}@{}",
child.tag_name().name(),
ns
);
}
}
None => {
writeln_indented!(
depth + 2,
s,
"tag_name: {}",
child.tag_name().name()
);
}
}
let attributes = child.attributes();
if attributes.len() != 0 {
let mut attrs = Vec::new();
for attr in attributes {
match attr.namespace() {
Some(ns) => {
attrs.push((format!("{}@{}", attr.name(), ns), attr.value()));
}
None => {
attrs.push((attr.name().to_string(), attr.value()));
}
}
}
attrs.sort_by(|a, b| a.0.cmp(&b.0));
writeln_indented!(depth + 2, s, "attributes:");
for (name, value) in attrs {
writeln_indented!(depth + 3, s, "{}: {:?}", name, value);
}
}
let namespaces = child.namespaces();
if namespaces.len() != 0 {
let mut ns_list = Vec::new();
for ns in namespaces {
let name = ns.name().unwrap_or("None");
let uri = if ns.uri().is_empty() {
"\"\""
} else {
ns.uri()
};
ns_list.push((name, uri));
}
ns_list.sort_by(|a, b| a.0.cmp(b.0));
writeln_indented!(depth + 2, s, "namespaces:");
for (name, uri) in ns_list {
writeln_indented!(depth + 3, s, "{}: {}", name, uri);
}
}
if child.has_children() {
writeln_indented!(depth + 2, s, "children:");
print_children(child, depth + 3, s)?;
}
}
NodeType::Text => {
writeln_indented!(depth, s, "- Text: {:?}", child.text().unwrap());
}
NodeType::Comment => {
writeln_indented!(depth, s, "- Comment: {:?}", child.text().unwrap());
}
NodeType::PI => {
if child.parent().unwrap().is_root() {
continue;
}
writeln_indented!(depth, s, "- PI:");
let pi = child.pi().unwrap();
writeln_indented!(depth + 2, s, "target: {:?}", pi.target);
if let Some(value) = pi.value {
writeln_indented!(depth + 2, s, "value: {:?}", value);
}
}
NodeType::Root => {}
}
}
Ok(())
}
writeln!(s, "Document:")?;
print_children(doc.root(), 1, s)?;
Ok(())
}
macro_rules! test {
($name:ident) => {
#[test]
fn $name() {
actual_test(&format!("tests/files/{}.xml", stringify!($name)))
}
};
}
test!(attrs_001);
test!(attrs_002);
test!(attrs_003);
test!(attrs_004);
test!(attrs_005);
test!(attrs_006);
test!(attrs_err_001);
test!(attrs_err_002);
test!(cdata_001);
test!(cdata_002);
test!(cdata_003);
test!(cdata_004);
test!(cdata_005);
test!(cdata_006);
test!(cdata_007);
test!(comments_001);
test!(elems_err_001);
test!(elems_err_002);
test!(entity_001);
test!(entity_002);
test!(entity_003);
test!(entity_004);
test!(entity_005);
test!(entity_006);
test!(entity_007);
test!(entity_008);
test!(entity_009);
test!(entity_010);
test!(entity_011);
test!(entity_012);
test!(entity_013);
test!(entity_014);
test!(entity_err_001);
test!(entity_err_002);
test!(entity_err_003);
test!(entity_err_004);
test!(entity_err_005);
test!(entity_err_006);
test!(entity_err_007);
test!(entity_err_008);
test!(entity_err_009);
test!(ns_001);
test!(ns_002);
test!(ns_003);
test!(ns_004);
test!(ns_005);
test!(ns_006);
test!(ns_007);
test!(ns_008);
test!(ns_009);
test!(ns_010);
test!(ns_011);
test!(ns_012);
test!(ns_013);
test!(ns_014);
test!(ns_015);
test!(ns_016);
test!(ns_017);
test!(ns_err_001);
test!(ns_err_002);
test!(ns_err_003);
test!(ns_err_004);
test!(ns_err_005);
test!(ns_err_006);
test!(ns_err_007);
test!(ns_err_008);
test!(ns_err_009);
test!(ns_err_010);
test!(ns_err_011);
test!(ns_err_012);
test!(ns_err_013);
test!(text_001);
test!(text_002);
test!(text_003);
test!(text_004);
test!(text_005);
test!(text_006);
test!(text_007);
test!(text_008);
test!(text_009);
test!(text_010);
test!(text_011);
test!(text_012);
test!(tree_001);
test!(tree_002);
// test!(tree_003); // unsupported
test!(tree_err_001);
test!(tree_err_002);
test!(tree_err_003);