use alloc::boxed::Box;
use alloc::collections::btree_map::Entry;
use alloc::collections::BTreeMap;
use alloc::collections::BTreeSet;
use alloc::format;
use alloc::vec::Vec;
use bytes::{BufMut, BytesMut};
use crate::error::{Error, ErrorContext, Result};
use crate::parser::{Event, XmlVersion, XMLNS_XML, XMLNS_XMLNS};
use crate::strings::{Name, Namespace, NcName, NcNameStr};
const XML_DECL: &[u8] = b"<?xml version='1.0' encoding='utf-8'?>\n";
pub const PREFIX_XML: &NcNameStr = unsafe { core::mem::transmute("xml") };
pub const PREFIX_XMLNS: &NcNameStr = unsafe { core::mem::transmute("xmlns") };
fn escape_text<'a, B: BufMut>(
out: &'a mut B,
data: &'a str,
attribute_context: bool,
) -> core::result::Result<(), rxml_validation::Error> {
let data = data.as_bytes();
let mut last_index = 0;
for i in 0..data.len() {
let ch = data[i];
let escaped: &[u8] = match ch {
b'\x00'..=b'\x08' | b'\x0b' | b'\x0c' | b'\x0e'..=b'\x1f' => {
return Err(rxml_validation::Error::InvalidChar(ch.into()));
}
b'"' if attribute_context => &b"""[..],
b'\'' if attribute_context => &b"'"[..],
b'<' => &b"<"[..],
b'>' => &b">"[..],
b'&' => &b"&"[..],
b'\r' => &b"
"[..],
b'\n' if attribute_context => &b"
"[..],
b'\t' if attribute_context => &b"	"[..],
b'\xbe' | b'\xbf' => {
if i >= 2 && data[i - 2] == b'\xef' && data[i - 1] == b'\xbf' {
let low_bit = ch & 0x01;
return Err(rxml_validation::Error::InvalidChar(unsafe {
char::from_u32_unchecked(0xfffe | low_bit as u32)
}));
} else {
continue;
}
}
_ => continue,
};
if i > last_index {
out.put_slice(&data[last_index..i]);
}
out.put_slice(escaped);
last_index = i + 1;
}
out.put_slice(&data[last_index..data.len()]);
Ok(())
}
#[derive(Debug)]
pub enum Item<'x> {
XmlDeclaration(XmlVersion),
ElementHeadStart(
Namespace<'x>,
&'x NcNameStr,
),
Attribute(
Namespace<'x>,
&'x NcNameStr,
&'x str,
),
ElementHeadEnd,
Text(&'x str),
ElementFoot,
}
impl Item<'_> {
fn name(&self) -> &'static str {
match self {
Self::XmlDeclaration { .. } => "xml declaration",
Self::ElementHeadStart { .. } => "element start",
Self::Attribute { .. } => "attribute",
Self::ElementHeadEnd => "element head end",
Self::Text { .. } => "text",
Self::ElementFoot => "element foot",
}
}
}
#[derive(Debug)]
pub enum PrefixError {
Undeclared,
}
pub trait TrackNamespace {
fn declare_fixed(&mut self, prefix: Option<&NcNameStr>, name: Namespace<'static>) -> bool;
fn declare_auto(&mut self, name: Namespace<'static>) -> (bool, Option<&NcNameStr>);
fn declare_with_auto_prefix(&mut self, name: Namespace<'static>) -> (bool, &NcNameStr);
fn get_prefix_or_default(
&self,
name: Namespace<'static>,
) -> core::result::Result<Option<&NcNameStr>, PrefixError>;
fn get_prefix(&self, name: Namespace<'static>)
-> core::result::Result<&NcNameStr, PrefixError>;
fn push(&mut self);
fn pop(&mut self);
fn new_default_declaration(&self) -> Option<&Namespace<'static>>;
fn new_prefix_declarations(
&self,
) -> Box<dyn Iterator<Item = (&Namespace<'static>, &NcNameStr)> + '_>;
}
#[derive(Debug, Default)]
pub struct SimpleNamespaces {
global_ns: BTreeMap<Namespace<'static>, NcName>,
global_ns_rev: BTreeSet<NcName>,
global_ns_ctr: usize,
default_ns_stack: Vec<Namespace<'static>>,
next_default_ns: Option<Namespace<'static>>,
temp_ns_ctr: usize,
temp_ns: BTreeMap<Namespace<'static>, NcName>,
temp_ns_rev: BTreeSet<NcName>,
}
impl SimpleNamespaces {
pub fn new() -> Self {
Self::default()
}
pub fn lookup_prefix(
&self,
prefix: Option<&NcNameStr>,
) -> core::result::Result<Namespace<'static>, PrefixError> {
match prefix {
Some(prefix) if prefix == PREFIX_XML => Ok(Namespace::XML),
Some(prefix) if prefix == PREFIX_XMLNS => Ok(Namespace::XMLNS),
Some(prefix) => {
for (decl_uri, decl_prefix) in self.temp_ns.iter().chain(self.global_ns.iter()) {
if decl_prefix == prefix {
return Ok(decl_uri.clone());
}
}
Err(PrefixError::Undeclared)
}
None => {
if let Some(uri) = self.next_default_ns.as_ref() {
return Ok(uri.clone());
}
match self.default_ns_stack.last() {
Some(uri) => Ok(uri.clone()),
_ => Err(PrefixError::Undeclared),
}
}
}
}
}
impl TrackNamespace for SimpleNamespaces {
fn declare_fixed(&mut self, prefix: Option<&NcNameStr>, name: Namespace<'static>) -> bool {
match prefix.as_ref() {
Some(v) if *v == PREFIX_XML => {
if name == XMLNS_XML {
return false;
}
panic!("xml is a reserved prefix")
}
Some(v) if *v == PREFIX_XMLNS => {
if name == XMLNS_XMLNS {
return false;
}
panic!("xmlns is a reserved prefix")
}
_ => {}
}
if name == XMLNS_XML {
panic!("{} must be bound to xml prefix", name)
}
if name == XMLNS_XMLNS {
panic!("{} must be bound to xmlns prefix", name)
}
match prefix {
Some(prefix) => {
if self.global_ns_rev.contains(prefix) {
panic!(
"prefix declaration conflicts with global prefix: {:?}",
prefix
)
}
if self.temp_ns_rev.contains(prefix) {
panic!("duplicate prefix: {:?}", prefix);
}
self.temp_ns.insert(name, prefix.to_ncname());
self.temp_ns_rev.insert(prefix.to_ncname());
true
}
None => {
if self.next_default_ns.is_some() {
panic!("duplicate default namespace")
}
self.next_default_ns = Some(name);
true
}
}
}
fn declare_auto(&mut self, name: Namespace<'static>) -> (bool, Option<&NcNameStr>) {
if name == XMLNS_XML {
return (false, Some(PREFIX_XML));
}
if name == XMLNS_XMLNS {
return (false, Some(PREFIX_XMLNS));
}
match self
.next_default_ns
.as_ref()
.or(self.default_ns_stack.last())
{
Some(default_name) if *default_name == name => return (false, None),
_ => (),
};
match self.temp_ns.entry(name) {
Entry::Occupied(o) => (false, Some(o.into_mut())),
Entry::Vacant(v_temp) => match self.global_ns.entry(v_temp.key().clone()) {
Entry::Occupied(o) => (false, Some(o.into_mut())),
Entry::Vacant(_) => {
match self.next_default_ns.as_ref() {
Some(_) => {
let ctr = self.temp_ns_ctr;
let temp_ns_prefix: NcName = format!("tns{}", ctr)
.try_into()
.expect("auto-generated prefix must always be valid");
if self.global_ns_rev.contains(&temp_ns_prefix) {
panic!(
"automatic prefix declaration conflicts with global prefix: {:?}",
temp_ns_prefix
)
}
if self.temp_ns_rev.contains(&temp_ns_prefix) {
panic!(
"automatic prefix declaration conflicts with local prefix: {:?}",
temp_ns_prefix
)
}
self.temp_ns_ctr += 1;
self.temp_ns_rev.insert(temp_ns_prefix.clone());
(true, Some(v_temp.insert(temp_ns_prefix)))
}
None => {
let name = v_temp.into_key();
let new = match self.default_ns_stack.last() {
Some(v) if v == &name => {
self.next_default_ns = Some(v.clone());
false
}
_ => {
let new = !name.is_none();
self.next_default_ns = Some(name);
new
}
};
(new, None)
}
}
}
},
}
}
fn declare_with_auto_prefix(&mut self, name: Namespace<'static>) -> (bool, &NcNameStr) {
if name == XMLNS_XML {
return (false, PREFIX_XML);
}
if name == XMLNS_XMLNS {
return (false, PREFIX_XMLNS);
}
match self.temp_ns.entry(name.clone()) {
Entry::Occupied(o) => (false, o.into_mut()),
Entry::Vacant(v) => match self.global_ns.entry(name.clone()) {
Entry::Occupied(o) => (false, o.into_mut()),
Entry::Vacant(_) => {
let ctr = self.temp_ns_ctr;
let temp_ns_prefix: NcName = format!("tns{}", ctr)
.try_into()
.expect("auto-generated prefix must always be valid");
if self.global_ns_rev.contains(&temp_ns_prefix) {
panic!(
"automatic prefix declaration conflicts with global prefix: {:?}",
temp_ns_prefix
)
}
if self.temp_ns_rev.contains(&temp_ns_prefix) {
panic!(
"automatic prefix declaration conflicts with local prefix: {:?}",
temp_ns_prefix
)
}
self.temp_ns_ctr += 1;
self.temp_ns_rev.insert(temp_ns_prefix.clone());
(true, v.insert(temp_ns_prefix))
}
},
}
}
fn get_prefix_or_default(
&self,
name: Namespace<'static>,
) -> core::result::Result<Option<&NcNameStr>, PrefixError> {
match self
.next_default_ns
.as_ref()
.or(self.default_ns_stack.last())
{
Some(default_name) if *default_name == name => Ok(None),
_ => Ok(Some(self.get_prefix(name)?)),
}
}
fn get_prefix(
&self,
name: Namespace<'static>,
) -> core::result::Result<&NcNameStr, PrefixError> {
self.temp_ns
.get(&name)
.or(self.global_ns.get(&name))
.map(|x| &**x)
.ok_or(PrefixError::Undeclared)
}
fn push(&mut self) {
match self.next_default_ns.take() {
None => {
let old = self
.default_ns_stack
.last()
.unwrap_or(Namespace::none())
.clone();
self.default_ns_stack.push(old);
}
Some(v) => self.default_ns_stack.push(v),
}
if self.default_ns_stack.len() == 1 {
core::mem::swap(&mut self.global_ns, &mut self.temp_ns);
core::mem::swap(&mut self.global_ns_rev, &mut self.temp_ns_rev);
self.global_ns_ctr = self.temp_ns_ctr;
}
self.temp_ns_ctr = self.global_ns_ctr;
self.temp_ns.clear();
self.temp_ns_rev.clear();
}
fn pop(&mut self) {
self.default_ns_stack.pop();
}
fn new_default_declaration(&self) -> Option<&Namespace<'static>> {
match self.next_default_ns.as_ref() {
Some(ns) if ns.is_none() && self.default_ns_stack.is_empty() => None,
other => other,
}
}
fn new_prefix_declarations(
&self,
) -> Box<dyn Iterator<Item = (&Namespace<'static>, &NcNameStr)> + '_> {
Box::new(self.temp_ns.iter().map(|(k, v)| (k, &**v)))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
enum EncoderState {
#[default]
Start,
Declared,
ElementHead,
Content,
EndOfDocument,
}
impl EncoderState {
fn ctx(&self) -> Option<ErrorContext> {
Some(match self {
Self::Start => ErrorContext::XmlDeclarationStart,
Self::Declared => ErrorContext::DocumentBegin,
Self::ElementHead => ErrorContext::Element,
Self::Content => ErrorContext::Text,
Self::EndOfDocument => ErrorContext::DocumentEnd,
})
}
}
#[derive(Default)]
pub struct Encoder<T> {
state: EncoderState,
qname_stack: Vec<Name>,
ns: T,
}
impl Encoder<SimpleNamespaces> {
pub fn new() -> Self {
Self::default()
}
}
impl<T: TrackNamespace> From<T> for Encoder<T> {
fn from(ns: T) -> Self {
Self {
state: EncoderState::Start,
qname_stack: Vec::new(),
ns,
}
}
}
impl<T: TrackNamespace> Encoder<T> {
fn encode_nsdecl<O: BufMut>(
prefix: Option<&NcNameStr>,
nsuri: &str,
output: &mut O,
) -> core::result::Result<(), rxml_validation::Error> {
match prefix {
Some(prefix) => {
output.put_slice(b" xmlns:");
output.put_slice(prefix.as_bytes());
output.put_slice(b"='");
}
None => {
output.put_slice(b" xmlns='");
}
}
escape_text(output, nsuri, true)?;
output.put_u8(b'\'');
Ok(())
}
pub fn ns_tracker(&self) -> &T {
&self.ns
}
pub fn ns_tracker_mut(&mut self) -> &mut T {
&mut self.ns
}
pub fn encode<O: BufMut>(&mut self, item: Item<'_>, output: &mut O) -> Result<()> {
if self.state == EncoderState::EndOfDocument {
return Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None));
}
match item {
Item::XmlDeclaration(XmlVersion::V1_0) => match self.state {
EncoderState::Start => {
output.put_slice(XML_DECL);
self.state = EncoderState::Declared;
Ok(())
}
_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
},
Item::ElementHeadStart(ref nsuri, local_name) => match self.state {
EncoderState::Start | EncoderState::Declared | EncoderState::Content => {
output.put_u8(b'<');
let (_, prefix) = self.ns.declare_auto(nsuri.clone().into_static());
let qname = match prefix {
Some(prefix) => {
output.put_slice(prefix.as_bytes());
output.put_u8(b':');
output.put_slice(local_name.as_bytes());
prefix.with_suffix(local_name)
}
None => {
output.put_slice(local_name.as_bytes());
local_name.to_name()
}
};
self.qname_stack.push(qname);
if let Some(name) = self.ns.new_default_declaration() {
Self::encode_nsdecl(None, name, output)?;
}
for (name, prefix) in self.ns.new_prefix_declarations() {
Self::encode_nsdecl(Some(prefix), name, output)?;
}
self.state = EncoderState::ElementHead;
Ok(())
}
_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
},
Item::Attribute(ref nsuri, local_name, value) => match self.state {
EncoderState::ElementHead => {
match nsuri.as_namespace_name() {
Some(v) => {
let (new, prefix) = self
.ns
.declare_with_auto_prefix(nsuri.clone().into_static());
if new {
Self::encode_nsdecl(Some(prefix), v, output)?;
}
output.put_u8(b' ');
output.put_slice(prefix.as_bytes());
output.put_u8(b':');
output.put_slice(local_name.as_bytes());
}
None => {
output.put_u8(b' ');
output.put_slice(local_name.as_bytes());
}
}
output.put_u8(b'=');
output.put_u8(b'\'');
escape_text(output, value, true)?;
output.put_u8(b'\'');
Ok(())
}
_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
},
Item::ElementHeadEnd => match self.state {
EncoderState::ElementHead => {
output.put_u8(b'>');
self.ns.push();
self.state = EncoderState::Content;
Ok(())
}
_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
},
Item::Text(cdata) => match self.state {
EncoderState::Content => {
escape_text(output, cdata, false)?;
Ok(())
}
_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
},
Item::ElementFoot => match self.state {
EncoderState::Content => {
self.ns.pop();
output.put_slice(b"</");
output.put_slice(self.qname_stack.pop().unwrap().as_bytes());
output.put_u8(b'>');
if self.qname_stack.is_empty() {
self.state = EncoderState::EndOfDocument
}
Ok(())
}
EncoderState::ElementHead => {
output.put_slice(b"/>");
self.ns.push();
self.ns.pop();
self.qname_stack.pop();
if self.qname_stack.is_empty() {
self.state = EncoderState::EndOfDocument
} else {
self.state = EncoderState::Content;
}
Ok(())
}
_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
},
}
}
pub fn encode_into_bytes(&mut self, item: Item<'_>, output: &mut BytesMut) -> Result<()> {
self.encode(item, output)
}
pub fn encode_event<O: BufMut>(&mut self, ev: &Event, output: &mut O) -> Result<()> {
match ev {
Event::XmlDeclaration(_, version) => {
self.encode(Item::XmlDeclaration(*version), output)?;
}
Event::StartElement(_, (ns, name), attrs) => {
self.encode(Item::ElementHeadStart(ns.borrow(), name.as_ref()), output)?;
for ((ns, name), v) in attrs.iter() {
self.encode(Item::Attribute(ns.borrow(), name.as_ref(), v), output)?
}
self.encode(Item::ElementHeadEnd, output)?;
}
Event::EndElement(_) => self.encode(Item::ElementFoot, output)?,
Event::Text(_, text) => self.encode(Item::Text(text.as_ref()), output)?,
}
Ok(())
}
pub fn encode_event_into_bytes(&mut self, ev: &Event, output: &mut BytesMut) -> Result<()> {
self.encode_event(ev, output)
}
}
#[cfg(test)]
mod tests_simple_namespaces {
use super::*;
fn ns1() -> Namespace<'static> {
Namespace::try_from("uri:foo").unwrap()
}
fn ns2() -> Namespace<'static> {
Namespace::try_from("uri:bar").unwrap()
}
fn ns3() -> Namespace<'static> {
Namespace::try_from("uri:baz").unwrap()
}
fn mk() -> SimpleNamespaces {
SimpleNamespaces::new()
}
#[test]
fn prefers_setting_default_namespace() {
let mut ns = mk();
let (new, prefix) = ns.declare_auto(ns1());
assert!(new);
assert!(prefix.is_none());
}
#[test]
fn unset_namespace_is_not_new_initially() {
let mut ns = mk();
let (new, prefix) = ns.declare_auto(Namespace::NONE);
assert!(prefix.is_none());
assert!(!new);
}
#[test]
fn predefined_prefixes_can_be_looked_up() {
let ns = mk();
assert_eq!(ns.lookup_prefix(Some(PREFIX_XML)).unwrap(), XMLNS_XML);
assert_eq!(ns.lookup_prefix(Some(PREFIX_XMLNS)).unwrap(), XMLNS_XMLNS);
}
#[test]
fn hardcoded_xmlns_namespace_for_declare_auto() {
let mut ns = mk();
let (new, prefix) = ns.declare_auto(Namespace::XMLNS);
assert!(!new);
assert!(prefix.unwrap() == PREFIX_XMLNS);
}
#[test]
fn hardcoded_xml_namespace_for_declare_auto() {
let mut ns = mk();
let (new, prefix) = ns.declare_auto(Namespace::XML);
assert!(!new);
assert!(prefix.unwrap() == PREFIX_XML);
}
#[test]
fn hardcoded_xmlns_namespace_for_declare_with_auto_prefix() {
let mut ns = mk();
let (new, prefix) = ns.declare_with_auto_prefix(Namespace::XMLNS);
assert!(!new);
assert!(prefix == "xmlns");
}
#[test]
fn hardcoded_xml_namespace_for_declare_with_auto_prefix() {
let mut ns = mk();
let (new, prefix) = ns.declare_with_auto_prefix(Namespace::XML);
assert!(!new);
assert!(prefix == "xml");
}
#[test]
fn hardcoded_xmlns_namespace_for_declare_fixed() {
let mut ns = mk();
let new = ns.declare_fixed(Some(PREFIX_XMLNS), Namespace::XMLNS);
assert!(!new);
}
#[test]
fn hardcoded_xml_namespace_for_declare_fixed() {
let mut ns = mk();
let new = ns.declare_fixed(Some(PREFIX_XML), Namespace::XML);
assert!(!new);
}
#[test]
#[should_panic(expected = "xml is a reserved prefix")]
fn reject_xml_as_fixed_prefix_for_random_namespace() {
let mut ns = mk();
ns.declare_fixed(Some(PREFIX_XML), ns1());
}
#[test]
#[should_panic(expected = "xmlns is a reserved prefix")]
fn reject_xmlns_fixed_prefix_for_random_namespace() {
let mut ns = mk();
ns.declare_fixed(Some(PREFIX_XMLNS), ns1());
}
#[test]
#[should_panic(expected = "must be bound to xml prefix")]
fn reject_xml_namespace_with_other_prefix() {
let mut ns = mk();
ns.declare_fixed(Some("foo".try_into().unwrap()), Namespace::XML);
}
#[test]
#[should_panic(expected = "must be bound to xmlns prefix")]
fn reject_xmlns_namespace_with_other_prefix() {
let mut ns = mk();
ns.declare_fixed(Some("foo".try_into().unwrap()), Namespace::XMLNS);
}
#[test]
fn retrieve_default_namespace() {
let mut ns = mk();
ns.declare_auto(ns1());
match ns.get_prefix_or_default(ns1()) {
Ok(None) => {}
other => panic!("unexpected get_prefix_or_default result: {:?}", other),
};
match ns.lookup_prefix(None) {
Ok(name) => {
assert_eq!(name, ns1());
}
other => panic!("unexpected lookup_prefix result: {:?}", other),
}
}
#[test]
fn declaration_causes_lookup_to_return_a_thing() {
let mut ns = mk();
match ns.lookup_prefix(None) {
Err(PrefixError::Undeclared) => (),
other => panic!("unexpected lookup_prefix result: {:?}", other),
}
ns.declare_auto(ns1());
match ns.lookup_prefix(None) {
Ok(name) => {
assert_eq!(name, ns1());
}
other => panic!("unexpected lookup_prefix result: {:?}", other),
}
}
#[test]
fn reuses_existing_declaration_if_available() {
let mut ns = mk();
ns.declare_auto(ns1());
let (new, prefix) = ns.declare_auto(ns1());
assert!(!new);
assert!(prefix.is_none());
}
#[test]
fn allocates_prefix_if_default_is_already_set() {
let mut ns = mk();
ns.declare_auto(ns1());
let (new, prefix) = ns.declare_auto(ns2());
assert!(new);
assert!(prefix.is_some());
}
#[test]
fn retrieve_prefixed_namespace() {
let mut ns = mk();
let prefix = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
match ns.get_prefix_or_default(ns1()) {
Ok(Some(v)) => {
assert_eq!(v, prefix);
}
other => panic!("unexpected get_prefix_or_default result: {:?}", other),
};
}
#[test]
fn declaration_causes_lookup_to_return_a_thing_for_a_prefix() {
let mut ns = mk();
match ns.lookup_prefix(None) {
Err(PrefixError::Undeclared) => (),
other => panic!("unexpected lookup_prefix result: {:?}", other),
}
let prefix = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
match ns.lookup_prefix(Some(&prefix)) {
Ok(name) => {
assert_eq!(name, ns1());
}
other => panic!("unexpected lookup_prefix result: {:?}", other),
}
match ns.lookup_prefix(None) {
Err(PrefixError::Undeclared) => (),
other => panic!("unexpected lookup_prefix result: {:?}", other),
}
}
#[test]
fn reuses_auto_allocated_prefix_for_same_ns() {
let mut ns = mk();
let prefix1 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
let prefix2 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
assert_eq!(prefix1, prefix2);
}
#[test]
fn auto_allocates_different_prefixes_for_different_ns() {
let mut ns = mk();
let prefix1 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
let prefix2 = ns.declare_with_auto_prefix(ns2()).1.to_ncname();
assert_ne!(prefix1, prefix2);
}
#[test]
fn preserves_default_ns_across_elements() {
let mut ns = mk();
ns.declare_auto(ns1());
ns.get_prefix_or_default(ns1()).unwrap();
ns.push();
let (new, prefix) = ns.declare_auto(ns1());
assert!(!new);
assert!(prefix.is_none());
assert!(ns.get_prefix_or_default(ns1()).unwrap().is_none());
match ns.lookup_prefix(None) {
Ok(name) => {
assert_eq!(name, ns1());
}
other => panic!("unexpected lookup_prefix result: {:?}", other),
}
}
#[test]
fn declare_with_fixed_prefix() {
let mut ns = mk();
ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns1());
match ns.get_prefix(ns1()) {
Ok(v) => {
assert_eq!(v, "stream");
}
other => panic!("unexpected get_prefix result: {:?}", other),
}
match ns.get_prefix_or_default(ns1()) {
Ok(Some(v)) => {
assert_eq!(v, "stream");
}
other => panic!("unexpected get_prefix result: {:?}", other),
}
}
#[test]
fn declare_fixed_prefixless() {
let mut ns = mk();
ns.declare_fixed(None, ns1());
match ns.get_prefix_or_default(ns1()) {
Ok(None) => (),
other => panic!("unexpected get_prefix result: {:?}", other),
}
match ns.get_prefix(ns1()) {
Err(PrefixError::Undeclared) => (),
other => panic!("unexpected get_prefix result: {:?}", other),
}
}
#[test]
fn preserves_global_prefixed_across_elements() {
let mut ns = mk();
ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns1());
ns.declare_fixed(None, ns2());
ns.push();
match ns.get_prefix(ns1()) {
Ok(v) => {
assert_eq!(v, "stream");
}
other => panic!("unexpected get_prefix result: {:?}", other),
}
match ns.get_prefix_or_default(ns1()) {
Ok(Some(v)) => {
assert_eq!(v, "stream");
}
other => panic!("unexpected get_prefix result: {:?}", other),
}
match ns.get_prefix_or_default(ns2()) {
Ok(None) => (),
other => panic!("unexpected get_prefix result: {:?}", other),
}
match ns.get_prefix(ns2()) {
Err(PrefixError::Undeclared) => (),
other => panic!("unexpected get_prefix result: {:?}", other),
}
}
#[test]
#[should_panic(expected = "conflict")]
fn prohibits_overriding_global_prefix() {
let mut ns = mk();
ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns1());
ns.declare_fixed(None, ns2());
ns.push();
ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns2());
}
#[test]
fn auto_allocations_stay_global_and_do_not_cause_conflicts() {
let mut ns = mk();
let prefix1 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
let prefix2 = ns.declare_with_auto_prefix(ns2()).1.to_ncname();
ns.push();
let (new, prefix3) = ns.declare_with_auto_prefix(ns3());
assert!(new);
let prefix3 = prefix3.to_ncname();
assert_ne!(prefix1, prefix3);
assert_ne!(prefix2, prefix3);
}
#[test]
#[should_panic(expected = "conflict")]
fn fixed_global_decl_can_conflict_with_auto_decl() {
let mut ns = mk();
ns.declare_fixed(Some(NcNameStr::from_str("tns0").unwrap()), ns1());
ns.push();
ns.declare_with_auto_prefix(ns3());
}
#[test]
#[should_panic(expected = "conflict")]
fn fixed_local_decl_can_conflict_with_auto_decl() {
let mut ns = mk();
ns.declare_fixed(Some(NcNameStr::from_str("tns0").unwrap()), ns1());
ns.declare_with_auto_prefix(ns3());
}
}
#[cfg(test)]
mod tests_encoder {
use alloc::string::String;
use std::io;
use std::println;
use super::*;
use crate::parser::EventMetrics;
fn mkencoder() -> Encoder<SimpleNamespaces> {
Encoder::new()
}
fn ns1() -> Namespace<'static> {
Namespace::try_from("uri:foo").unwrap()
}
fn ns2() -> Namespace<'static> {
Namespace::try_from("uri:bar").unwrap()
}
fn ns3() -> Namespace<'static> {
Namespace::try_from("uri:baz").unwrap()
}
fn parse(mut input: &[u8]) -> (Vec<Event>, io::Result<bool>) {
let mut parser: crate::Reader<_> = crate::Reader::new(&mut input);
let mut events = Vec::new();
let result = crate::as_eof_flag(parser.read_all(|ev| events.push(ev)));
(events, result)
}
fn encode_events(evs: &[Event]) -> Result<BytesMut> {
let mut out = BytesMut::new();
let mut encoder = mkencoder();
for ev in evs {
encoder.encode_event(&ev, &mut out)?;
}
Ok(out)
}
fn encode_events_via_into_bytes(evs: &[Event]) -> Result<BytesMut> {
let mut out = BytesMut::new();
let mut encoder = mkencoder();
for ev in evs {
encoder.encode_event_into_bytes(&ev, &mut out)?;
}
Ok(out)
}
fn collapse_cdata(evs: &mut Vec<Event>) {
let mut buf = Vec::new();
core::mem::swap(&mut buf, evs);
let mut cdata_hold = None;
for event in buf.drain(..) {
match event {
Event::Text(_, txt) => match cdata_hold.take() {
None => cdata_hold = Some(txt),
Some(existing) => cdata_hold = Some(existing + &*txt),
},
_ => {
match cdata_hold.take() {
Some(txt) => evs.push(Event::Text(EventMetrics::new(0), txt)),
None => (),
};
evs.push(event);
}
}
}
match cdata_hold.take() {
Some(txt) => evs.push(Event::Text(EventMetrics::new(0), txt)),
None => (),
};
}
fn assert_event_eq(a: &Event, b: &Event) {
match (a, b) {
(Event::XmlDeclaration(_, v1), Event::XmlDeclaration(_, v2)) => {
assert_eq!(v1, v2);
}
(Event::StartElement(_, name1, attrs1), Event::StartElement(_, name2, attrs2)) => {
assert_eq!(name1, name2);
assert_eq!(attrs1, attrs2);
}
(Event::EndElement(_), Event::EndElement(_)) => {}
(Event::Text(_, text1), Event::Text(_, text2)) => {
assert_eq!(text1, text2);
}
(a, b) => panic!("event types differ: {:?} != {:?}", a, b),
}
}
fn assert_events_eq(initial: &[Event], reparsed: &[Event]) {
for (a, b) in initial.iter().zip(reparsed.iter()) {
assert_event_eq(a, b);
}
if initial.len() > reparsed.len() {
panic!(
"missing {} events in reparsed version",
initial.len() - reparsed.len()
)
}
if reparsed.len() > initial.len() {
panic!(
"{} additional events in reparsed version: {:?}",
reparsed.len() - initial.len(),
&reparsed[initial.len()..]
)
}
}
fn check_reserialized(
input: &[u8],
initial: &[Event],
initial_eof: bool,
reserialized: &[u8],
via: &'static str,
) {
let (mut reparsed, reparsed_result) = parse(&reserialized[..]);
collapse_cdata(&mut reparsed);
let reparsed_eof = match reparsed_result {
Ok(eof) => eof,
Err(e) => {
panic!(
"reserialized (via {}) XML\n\n{:?}\n\n of\n\n{:?}\n\nfails to parse: {}",
via,
String::from_utf8_lossy(&reserialized[..]),
String::from_utf8_lossy(input),
e
)
}
};
println!("checking (via {})", via);
println!(
"reserialized: {:?}",
String::from_utf8_lossy(&reserialized[..])
);
assert_events_eq(initial, &reparsed);
assert_eq!(initial_eof, reparsed_eof);
}
fn roundtrip_test(input: &[u8]) {
let (mut initial, initial_result) = parse(input);
collapse_cdata(&mut initial);
let initial_eof = initial_result.expect("test input must parse correctly");
let reserialized_via_buf =
encode_events(&initial[..]).expect("parsed input must be encodable");
let reserialized_via_bytes =
encode_events_via_into_bytes(&initial[..]).expect("parsed input must be encodable");
check_reserialized(
input,
&initial,
initial_eof,
&reserialized_via_buf[..],
"buf",
);
check_reserialized(
input,
&initial,
initial_eof,
&reserialized_via_bytes[..],
"bytes",
);
}
#[test]
fn reject_duplicate_xml_declaration() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf) {
Err(Error::UnexpectedToken(_, "xml declaration", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn reject_text_at_global_level() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(Item::Text("".try_into().unwrap()), &mut buf) {
Err(Error::UnexpectedToken(_, "text", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn reject_attribute_at_global_level() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::Attribute(
Namespace::NONE,
"x".try_into().unwrap(),
"".try_into().unwrap(),
),
&mut buf,
) {
Err(Error::UnexpectedToken(_, "attribute", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn allow_element_before_decl() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn reject_xml_decl_in_element() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf) {
Err(Error::UnexpectedToken(_, "xml declaration", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn encode_self_closed_tag() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
assert_eq!(&buf, &b"<x/>"[..]);
}
#[test]
fn encode_root_prefix() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
match enc.encode(
Item::ElementHeadStart(ns1(), "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
assert_eq!(&buf, &b"<foo:x xmlns:foo='uri:foo'/>"[..]);
}
#[test]
fn use_explicitly_set_at_root() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
match enc.encode(
Item::ElementHeadStart(ns2(), "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::ElementHeadStart(ns1(), "y".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
assert_eq!(
&buf,
&b"<x xmlns='uri:bar' xmlns:foo='uri:foo'><foo:y/></x>"[..]
);
}
#[test]
fn do_not_emit_duplicate_declarations() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
match enc.encode(
Item::ElementHeadStart(ns2(), "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::Attribute(ns1(), "a1".try_into().unwrap(), "v1".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns1(), "a2".try_into().unwrap(), "v2".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns2(), "a3".try_into().unwrap(), "v3".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns2(), "a4".try_into().unwrap(), "v4".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns3(), "a5".try_into().unwrap(), "v5".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns3(), "a6".try_into().unwrap(), "v6".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::ElementHeadStart(ns1(), "y".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
assert_eq!(&buf, &b"<x xmlns='uri:bar' xmlns:foo='uri:foo' foo:a1='v1' foo:a2='v2' xmlns:tns0='uri:bar' tns0:a3='v3' tns0:a4='v4' xmlns:tns1='uri:baz' tns1:a5='v5' tns1:a6='v6'><foo:y/></x>"[..]);
}
#[test]
fn reuses_existing_declaration_for_nested_namespaced_attributes_if_available() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
match enc.encode(
Item::ElementHeadStart(ns2(), "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::Attribute(ns1(), "a1".try_into().unwrap(), "v1".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns1(), "a2".try_into().unwrap(), "v2".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns2(), "a3".try_into().unwrap(), "v3".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns2(), "a4".try_into().unwrap(), "v4".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns3(), "a5".try_into().unwrap(), "v5".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(
Item::Attribute(ns3(), "a6".try_into().unwrap(), "v6".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::ElementHeadStart(ns1(), "y".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::Attribute(ns1(), "a1".try_into().unwrap(), "v1".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
}
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
assert_eq!(&buf, &b"<x xmlns='uri:bar' xmlns:foo='uri:foo' foo:a1='v1' foo:a2='v2' xmlns:tns0='uri:bar' tns0:a3='v3' tns0:a4='v4' xmlns:tns1='uri:baz' tns1:a5='v5' tns1:a6='v6'><foo:y foo:a1='v1'/></x>"[..]);
}
#[test]
fn self_closed_tag_encoding_ends_document_if_it_closes_the_root() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::Text("".try_into().unwrap()), &mut buf) {
Err(Error::UnexpectedToken(Some(ErrorContext::DocumentEnd), "text", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn self_closed_tag_encoding_allows_content_thereafter() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "y".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::Text("foo".try_into().unwrap()), &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
assert_eq!(&buf[..], b"<x><y/>foo");
}
#[test]
fn reject_element_after_end_of_document() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Err(Error::UnexpectedToken(Some(ErrorContext::DocumentEnd), "element start", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn reject_element_foot_before_start() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(Item::ElementFoot, &mut buf) {
Err(Error::UnexpectedToken(_, "element foot", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn reject_element_head_end_outside_element_header() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Err(Error::UnexpectedToken(_, "element head end", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Err(Error::UnexpectedToken(_, "element head end", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn reject_text_after_end_of_document() {
let mut enc = mkencoder();
let mut buf = BytesMut::new();
match enc.encode(
Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
&mut buf,
) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementHeadEnd, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::ElementFoot, &mut buf) {
Ok(()) => (),
other => panic!("unexpected encode result: {:?}", other),
};
match enc.encode(Item::Text("".try_into().unwrap()), &mut buf) {
Err(Error::UnexpectedToken(Some(ErrorContext::DocumentEnd), "text", _)) => (),
other => panic!("unexpected encode result: {:?}", other),
};
}
#[test]
fn single_element_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a/>")
}
#[test]
fn nested_element_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a><b/></a>")
}
#[test]
fn mixed_content_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a>foo<b>bar</b>baz</a>")
}
#[test]
fn text_with_lt_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a><</a>")
}
#[test]
fn text_with_gt_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a>></a>")
}
#[test]
fn text_with_escaped_entity_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a>&amp;</a>")
}
#[test]
fn text_cdata_sequence_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a>]]></a>")
}
#[test]
fn attribute_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a a1='foo' a2=\"bar\"/>")
}
#[test]
fn attribute_whitespace_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a a1='
	 '/>")
}
#[test]
fn attribute_quotes_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a a1='"''/>")
}
#[test]
fn namespace_roundtrip() {
roundtrip_test(b"<?xml version='1.0'?>\n<a xmlns='uri:foo'/>")
}
#[test]
fn namespace_roundtrip_builtins() {
roundtrip_test(b"<?xml version='1.0'?>\n<a xml:lang='de'/>")
}
#[test]
fn namespace_roundtrip_with_prefixes() {
roundtrip_test(
b"<?xml version='1.0'?>\n<a xmlns='uri:foo' xmlns:b='uri:bar'><b:b><c/></b:b></a>",
)
}
#[test]
fn roundtrip_escaped_crlf() {
roundtrip_test(b"<?xml version='1.0'?>\n<a>\r\n
</a>")
}
#[test]
fn prefixed_attribute_roundtrip() {
roundtrip_test(
b"<?xml version='1.0'?>\n<a xmlns:b='uri:foo' b:a1='baz' a2='fnord' b:a3='foobar'/>",
)
}
#[test]
fn escape_text_rejects_non_xml_chars() {
let mut buf = String::with_capacity(4);
let mut sink = BytesMut::with_capacity(8);
for cp in 0x0..=0x10ffffu32 {
if let Some(ch) = char::from_u32(cp) {
buf.clear();
buf.push(ch);
let validation = rxml_validation::validate_cdata(&buf);
assert_eq!(
{
sink.clear();
escape_text(&mut sink, &buf, false)
},
validation,
"in non-attribute context",
);
assert_eq!(
{
sink.clear();
escape_text(&mut sink, &buf, true)
},
validation,
"in attribute context",
);
}
}
}
}