/*!
# Writer for restricted XML 1.0
*/
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";
/// The static prefix used for XML built-in attributes (`xml`).
pub const PREFIX_XML: &NcNameStr = unsafe { core::mem::transmute("xml") };
/// The static prefix used for XML namespace declarations (`xmlns`).
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' {
// U+FFFE or U+FFFF
let low_bit = ch & 0x01;
// SAFETY: we are passing only 16 bits and the upper
// nibble is set to all ones, so this is within the bounds
// of a unicode code point and not a surrogate.
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(())
}
/// An encodable item.
///
/// This is separate from [`Event`], because events are owned, while
/// items can be borrowed to improve efficiency (as a copy will have to take
/// place anyway).
///
/// [`Event`]: crate::parser::Event
#[derive(Debug)]
pub enum Item<'x> {
/// XML declaration
XmlDeclaration(XmlVersion),
/// Start of an element header
ElementHeadStart(
/// Namespace name
Namespace<'x>,
/// Local name of the attribute
&'x NcNameStr,
),
/// An attribute key/value pair
Attribute(
/// Namespace name
Namespace<'x>,
/// Local name of the attribute
&'x NcNameStr,
/// Value of the attribute
&'x str,
),
/// End of an element header
ElementHeadEnd,
/// A piece of text (in element content, not attributes)
Text(&'x str),
/// Footer of an element
///
/// This can be used either in places where [`Text`] could be used to
/// close the most recently opened unclosed element, or it can be used
/// instead of [`ElementHeadEnd`] to close the element using `/>`, without
/// any child content.
///
/// [`Text`]: Self::Text
/// [`ElementHeadEnd`]: Self::ElementHeadEnd
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",
}
}
}
/// Represent an error returned by a prefix operation on [`TrackNamespace`].
#[derive(Debug)]
pub enum PrefixError {
/// The requested prefix is not declared yet.
Undeclared,
}
/// Trait for a thing tracking namespace declarations.
///
/// Indirection via this trait allows to have different paradigms for
/// declaring and managing namespace prefixes.
///
/// Objects implementing this trait expect the following protocol:
///
/// 1. Declare all namespace URIs introduced on an element using `declare` and `declare_auto`.
/// 2. Commit to the element using `push`
/// 3. Process all child elements by recursion
/// 4. Call `pop` to end the element.
///
/// Asymmetric calls to push/pop may cause panics or memory leaks.
pub trait TrackNamespace {
/// Declare a namespace URI with a defined prefix.
///
/// Note: There is no guarantee that the given `prefix` will be returned
/// from calls to `get_prefix` or `get_prefix_or_default` after the next
/// call to `push`.
///
/// Returns whether the prefix is freshly declared.
///
/// # Panics
///
/// Calling this twice between two calls to `push` with the same `prefix`
/// is a programming error and causes a panic.
fn declare_fixed(&mut self, prefix: Option<&NcNameStr>, name: Namespace<'static>) -> bool;
/// Declare a namespace URI with an auto-generated prefix or by using the
/// default namespace.
///
/// Note: There is no guarantee that the returned `prefix` will be
/// returned from calls to `get_prefix_or_default` after the next call to
/// `push`.
///
/// Returns whether the prefix is freshly declared and the
/// resulting prefix (or None, if prefixless).
///
/// This may return a non-auto-generated prefix if the namespace URI is
/// already declared on this or a parent element.
fn declare_auto(&mut self, name: Namespace<'static>) -> (bool, Option<&NcNameStr>);
/// Declare a namespace URI with an auto-generated prefix.
///
/// Note: There is no guarantee that the returned `prefix` will be
/// returned from calls to `get_prefix` or `get_prefix_or_default` after
/// the next call to `push`.
///
/// Returns whether the prefix is freshly declared and the resulting
/// prefix.
///
/// This may return a non-auto-generated prefix if the namespace URI is
/// already declared on this or a parent element. If the URI is already
/// used for the default namespace, this function will nontheless return
/// a prefix.
fn declare_with_auto_prefix(&mut self, name: Namespace<'static>) -> (bool, &NcNameStr);
/// Get the prefix for a given URI, which may be empty if the namespace
/// with that URI is defined as the default namespace.
fn get_prefix_or_default(
&self,
name: Namespace<'static>,
) -> core::result::Result<Option<&NcNameStr>, PrefixError>;
/// Get the prefix for a given URI.
///
/// This returns an error if the given URI is declared as default
/// namespace and there is no matching prefix.
fn get_prefix(&self, name: Namespace<'static>)
-> core::result::Result<&NcNameStr, PrefixError>;
/// Complete an element declaration.
fn push(&mut self);
/// Signal end of element to undeclare nested namespace declarations.
fn pop(&mut self);
/// Return the newly declared default namespace, if any.
///
/// This returns `None` if and only if no default namespace has been
/// declared yet for the upcoming element.
///
/// *Note:* In the root element, `None` will be returned even if the
/// default namespace has been explicitly set to the empty namespace name,
/// as that is the default.
fn new_default_declaration(&self) -> Option<&Namespace<'static>>;
/// Return an iterator over the the newly declared prefixes.
fn new_prefix_declarations(
&self,
) -> Box<dyn Iterator<Item = (&Namespace<'static>, &NcNameStr)> + '_>;
}
/// Simple namespace tracker.
///
/// This is the default namespace tracker used by [`Encoder::new`]. At the
/// cost of increased output size, it reduces memory footprint by only
/// tracking unprefixed namespaces. Prefixed namespaces may be declared, but
/// are forgotten about once the element start is over.
///
/// Effectively, when used with an [`Encoder`], this means that prefixed
/// namespaces will only ever be used for attributes, and may be re-declared
/// a lot.
///
/// One exception is that prefixed namespaces declared on the root element
/// will actually be made available on all child elements.
#[derive(Debug, Default)]
pub struct SimpleNamespaces {
// persistent state
global_ns: BTreeMap<Namespace<'static>, NcName>,
global_ns_rev: BTreeSet<NcName>,
global_ns_ctr: usize,
default_ns_stack: Vec<Namespace<'static>>,
// temporary per-element state
next_default_ns: Option<Namespace<'static>>,
temp_ns_ctr: usize,
temp_ns: BTreeMap<Namespace<'static>, NcName>,
temp_ns_rev: BTreeSet<NcName>,
}
impl SimpleNamespaces {
/// Create a new, empty namespace tracker.
pub fn new() -> Self {
Self::default()
}
/// Look up the namespace URI for a given prefix
///
/// *Note:* This function is implemented as O(n) function because it
/// should rarely, if ever, be necessary to use. Speeding up runtime of
/// this function would increase memory cost at neglegible gain.
///
/// The namespace URIs for the `xml` and `xmlns` prefixes are always
/// returned, even if not explicitly declared.
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));
}
// XXX: due to a limitation in the borrowchecker <https://github.com/rust-lang/rfcs/blob/master/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions>, we have to re-implement the entirety of get_prefix_or_default here.
// Check if the namespace has been declared without prefix
match self
.next_default_ns
.as_ref()
.or(self.default_ns_stack.last())
{
// .. it has, return it prefixless.
Some(default_name) if *default_name == name => return (false, None),
// .. it has not, move on.
_ => (),
};
// Check if the namespace has been declared locally already
match self.temp_ns.entry(name) {
// .. it has, return the prefix.
Entry::Occupied(o) => (false, Some(o.into_mut())),
// .. it has not, check if it has been declared globally
Entry::Vacant(v_temp) => match self.global_ns.entry(v_temp.key().clone()) {
// .. it has, return the prefix.
Entry::Occupied(o) => (false, Some(o.into_mut())),
// .. it has not, declare it.
Entry::Vacant(_) => {
// checked above already that it does not match the default namespace, but we need to see whether we can use the default namespace.
match self.next_default_ns.as_ref() {
// the default namespace has been declared at this element already, so we need to create a prefix.
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)))
}
// the default namespace has *not* been declared at this element yet, so we use it.
None => {
let name = v_temp.into_key();
let new = match self.default_ns_stack.last() {
Some(v) if v == &name => {
// re-use the previous instad of cloning this one, might be more efficient.
self.next_default_ns = Some(v.clone());
// it wasn't new.
false
}
_ => {
// treat it as new iff it is in fact a namespace
// this is to correctly return `new == false` if `declare_auto` is called with the empty namespace name on the root element.
// this can only happen on the root element, because otherwise default_ns_stack would have had an element and we wouldn't be in this branch.
let new = !name.is_none();
// assign the new object as next default namespace
self.next_default_ns = Some(name);
new
}
};
(new, None)
}
}
}
},
}
}
<<<<<<< Updated upstream
fn declare_with_auto_prefix(&mut self, name: Namespace<'static>) -> (bool, &NcNameStr) {
=======
fn declare_with_auto_prefix(&mut self, mut name: Namespace) -> (bool, &NcNameStr) {
>>>>>>> Stashed changes
if name == XMLNS_XML {
return (false, PREFIX_XML);
}
if name == XMLNS_XMLNS {
return (false, PREFIX_XMLNS);
}
match self.temp_ns.entry(name.share()) {
Entry::Occupied(o) => (false, o.into_mut()),
Entry::Vacant(v) => match self.global_ns.entry(name.share()) {
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 => {
// duplicate the most recent one
// if there is no previous one, we go with the default namespace.
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 {
// the first element! globalize the declarations
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() {
// if this is the root element, we do not expose the empty default namespace as declaration
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,
})
}
}
/**
Encodes XML into buffers.
Encoders are stateful. They can only be used to encode a single XML document and have then to be disposed.
```rust
use rxml::{Encoder, Item, XmlVersion};
use bytes::BytesMut;
let mut enc = Encoder::new();
let mut buf = BytesMut::new();
enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf);
assert_eq!(&buf[..], b"<?xml version='1.0' encoding='utf-8'?>\n");
```
*/
#[derive(Default)]
pub struct Encoder<T> {
state: EncoderState,
qname_stack: Vec<Name>,
ns: T,
}
impl Encoder<SimpleNamespaces> {
/// Create a new default encoder.
///
/// This encoder uses the [`SimpleNamespaces`] strategy, which is not
/// optimal with respect to the number of bytes written, but has reduced
/// memory cost.
pub fn new() -> Self {
Self::default()
}
}
impl<T: TrackNamespace> From<T> for Encoder<T> {
/// Create an encoder from a [`TrackNamespace`] instance.
///
/// Use your own custom namespace strategy.
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(())
}
/// Access the namespace tracking implementation.
pub fn ns_tracker(&self) -> &T {
&self.ns
}
/// Access the namespace tracking implementation, mutably.
pub fn ns_tracker_mut(&mut self) -> &mut T {
&mut self.ns
}
/// Encode a single item into a buffer.
///
/// There is no requirement for the buffer to be the same for subsequent
/// calls to this function. This allows users to use small, but
/// long-lived, buffers for serialization before sending data over the
/// network, for instance.
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() {
// if new, we have to declare it
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)),
},
}
}
/// Encode a single item into a BytesMut.
///
/// This might have a slight performance advantage over
/// [`encode`] in some scenarios, as it might be able to give
/// the BytesMut a heads up about required space, thus avoiding frequent
/// reallocations.
///
/// There is no requirement for the buffer to be the same for subsequent
/// calls to this function. This allows users to use small, but
/// long-lived, buffers for serialization before sending data over the
/// network, for instance.
///
/// [`encode`]: Self::encode
pub fn encode_into_bytes(&mut self, item: Item<'_>, output: &mut BytesMut) -> Result<()> {
self.encode(item, output)
}
/// Encode a single event into a buffer.
///
/// This internally decomposes the event into multiple items and then
/// encodes these into the given buffer using [`encode`].
///
/// [`encode`]: Self::encode.
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(())
}
/// Encode a single event into a BytesMut.
///
/// This internally decomposes the event into multiple items and then
/// encodes these into the given buffer using [`encode_into_bytes`].
///
/// [`encode_into_bytes`]: Self::encode_into_bytes.
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);
}
// will always raise
(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]) {
// goal: test that a parsed thing can be serialized again and then parsed to the semantically equivalent series of events
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",
);
}
}
}
}