#[cfg(doc)]
use crate::Xot;
use std::io::Write;
use crate::NameId;
use super::Indentation;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Parameters {
pub indentation: Option<Indentation>,
pub cdata_section_elements: Vec<NameId>,
pub declaration: Option<Declaration>,
pub doctype: Option<DocType>,
pub unescaped_gt: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Declaration {
pub encoding: Option<String>,
pub standalone: Option<bool>,
}
impl Declaration {
pub(crate) fn serialize(&self, w: &mut impl Write) -> Result<(), std::io::Error> {
w.write_all(b"<?xml version=\"1.0\"")?;
if let Some(encoding) = &self.encoding {
w.write_all(b" encoding=\"")?;
w.write_all(encoding.as_bytes())?;
w.write_all(b"\"")?;
}
if let Some(standalone) = self.standalone {
w.write_all(b" standalone=\"")?;
w.write_all(if standalone { b"yes" } else { b"no" })?;
w.write_all(b"\"")?;
}
w.write_all(b"?>\n")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DocType {
Public {
public: String,
system: String,
},
System {
system: String,
},
}
impl DocType {
pub(crate) fn serialize(&self, name: &str, w: &mut impl Write) -> Result<(), std::io::Error> {
w.write_all(b"<!DOCTYPE ")?;
w.write_all(name.as_bytes())?;
match self {
DocType::Public { public, system } => {
w.write_all(b" PUBLIC \"")?;
w.write_all(public.as_bytes())?;
w.write_all(b"\" \"")?;
w.write_all(system.as_bytes())?;
w.write_all(b"\"")?;
}
DocType::System { system } => {
w.write_all(b" SYSTEM \"")?;
w.write_all(system.as_bytes())?;
w.write_all(b"\"")?;
}
}
w.write_all(b">\n")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{output::Indentation, Xot};
use super::*;
#[test]
fn test_xml_output_default() {
let m = Parameters {
..Default::default()
};
let mut xot = Xot::new();
let doc = xot.parse("<doc><p>hello</p></doc>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<doc><p>hello</p></doc>"#
);
}
#[test]
fn test_xml_output_indent() {
let m = Parameters {
indentation: Some(Default::default()),
..Default::default()
};
let mut xot = Xot::new();
let doc = xot.parse("<doc><p>hello</p></doc>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<doc>
<p>hello</p>
</doc>
"#
);
}
#[test]
fn test_xml_output_indent_not_suppress() {
let mut xot = Xot::new();
let m = Parameters {
indentation: Some(Indentation { suppress: vec![] }),
..Default::default()
};
let doc = xot.parse("<doc><p><k>foo</k></p></doc>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<doc>
<p>
<k>foo</k>
</p>
</doc>
"#
);
}
#[test]
fn test_xml_output_indent_suppress() {
let mut xot = Xot::new();
let p = xot.add_name("p");
let m = Parameters {
indentation: Some(Indentation { suppress: vec![p] }),
..Default::default()
};
let doc = xot.parse("<doc><p><k>foo</k></p></doc>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<doc>
<p><k>foo</k></p>
</doc>
"#
);
}
#[test]
fn test_xml_output_declaration() {
let m = Parameters {
declaration: Some(Default::default()),
..Default::default()
};
let mut xot = Xot::new();
let doc = xot.parse("<doc/>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<?xml version="1.0"?>
<doc/>"#
);
}
#[test]
fn test_xml_output_declaration_standalone() {
let m = Parameters {
declaration: Some(Declaration {
standalone: Some(true),
..Default::default()
}),
..Default::default()
};
let mut xot = Xot::new();
let doc = xot.parse("<doc/>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<?xml version="1.0" standalone="yes"?>
<doc/>"#
);
}
#[test]
fn test_xml_output_doctype_public() {
let m = Parameters {
doctype: Some(DocType::Public {
public: "-//W3C//DTD XHTML 1.0 Strict//EN".to_string(),
system: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".to_string(),
}),
..Default::default()
};
let mut xot = Xot::new();
let doc = xot.parse("<doc/>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<!DOCTYPE doc PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<doc/>"#
);
}
#[test]
fn test_xml_output_doctype_system() {
let m = Parameters {
doctype: Some(DocType::System {
system: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".to_string(),
}),
..Default::default()
};
let mut xot = Xot::new();
let doc = xot.parse("<doc/>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<!DOCTYPE doc SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<doc/>"#
);
}
#[test]
fn test_xml_output_doctype_prefixed_name() {
let m = Parameters {
doctype: Some(DocType::System {
system: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".to_string(),
}),
..Default::default()
};
let mut xot = Xot::new();
let doc = xot.parse(r#"<prefix:doc xmlns:prefix="foo"/>"#).unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<!DOCTYPE prefix:doc SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<prefix:doc xmlns:prefix="foo"/>"#
);
}
#[test]
fn test_cdata_sections_elements() {
let mut xot = Xot::new();
let p = xot.add_name("p");
let m = Parameters {
cdata_section_elements: vec![p],
..Default::default()
};
let doc = xot.parse("<doc><p>hello</p></doc>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<doc><p><![CDATA[hello]]></p></doc>"#
);
}
#[test]
fn test_cdata_sections_elements_multiple() {
let mut xot = Xot::new();
let p = xot.add_name("p");
let m = Parameters {
cdata_section_elements: vec![p],
..Default::default()
};
let doc = xot.parse("<doc><p>hello<s> </s>world</p></doc>").unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<doc><p><![CDATA[hello]]><s> </s><![CDATA[world]]></p></doc>"#
);
}
#[test]
fn test_cdata_sections_elements_with_end_characters() {
let mut xot = Xot::new();
let p = xot.add_name("p");
let m = Parameters {
cdata_section_elements: vec![p],
..Default::default()
};
let doc = xot.parse(r#"<doc><p>hello]]> world</p></doc>"#).unwrap();
assert_eq!(
xot.serialize_xml_string(m, doc).unwrap(),
r#"<doc><p><![CDATA[hello]]]]><![CDATA[> world]]></p></doc>"#
);
}
}