Skip to main content

oxixml_xml/events/
mod.rs

1//! Zero-copy XML events used throughout this library.
2//!
3//! An XML event often represents part of an XML element. For example, the element
4//!
5//! ```xml
6//! <name attr="value">Inner text</name>
7//! ```
8//!
9//! consists of three events: [`Event::Start`], [`Event::Text`] and [`Event::End`].
10//! Events can also represent other parts of an XML document, like the XML
11//! declaration. See [`Event`] for the list of all possible events.
12//!
13//! These types mirror quick-xml 0.41 `events`: the [`Event`] enum plus the
14//! `Bytes*` payload structs. They occur both when reading and when writing.
15//!
16//! # Example
17//!
18//! ```
19//! use oxixml_xml::events::{BytesStart, Event};
20//! use oxixml_xml::name::QName;
21//!
22//! let start = BytesStart::new("name").with_attributes([("attr", "value")]);
23//! assert_eq!(start.name(), QName(b"name"));
24//! let event = Event::Start(start);
25//! // Events borrow their payload bytes.
26//! assert_eq!(&*event, b"name attr=\"value\"".as_ref());
27//! ```
28
29pub mod attributes;
30
31use std::borrow::Cow;
32use std::fmt::{self, Debug, Formatter};
33use std::iter::FusedIterator;
34use std::mem::replace;
35use std::ops::Deref;
36use std::str::from_utf8;
37
38#[cfg(feature = "encoding")]
39use oxixml_encoding::Encoding;
40
41use crate::XmlVersion;
42use crate::encoding::{Decoder, EncodingError};
43use crate::errors::{Error, IllFormedError};
44use crate::escape::{
45    EscapeError, escape, minimal_escape, normalize_xml10_eols, normalize_xml11_eols, parse_number,
46    partial_escape,
47};
48use crate::name::{LocalName, QName};
49use crate::utils::{self, name_len, trim_xml_end, trim_xml_start, write_cow_string};
50use attributes::{AttrError, Attribute, Attributes};
51
52/// Opening tag data ([`Event::Start`]), with optional attributes: `<name attr="value">`.
53///
54/// The name can be accessed using the [`name`](Self::name) or
55/// [`local_name`](Self::local_name) methods, and an iterator over the attributes
56/// is returned by [`attributes`](Self::attributes).
57///
58/// This event dereferences to the tag content between `<` and `>` (or `/>`).
59///
60/// ```
61/// # use oxixml_xml::events::BytesStart;
62/// let content = "element a1 = 'val1' a2=\"val2\" ";
63/// let event = BytesStart::from_content(content, 7);
64/// assert_eq!(event.name().as_ref(), b"element");
65/// // deref coercion of &BytesStart to &[u8]
66/// assert_eq!(&event as &[u8], content.as_bytes());
67/// assert_eq!(event.as_ref(), content.as_bytes());
68/// ```
69#[derive(Clone, Eq, PartialEq)]
70pub struct BytesStart<'a> {
71    /// Content of the element, before any UTF-8 conversion.
72    pub(crate) buf: Cow<'a, [u8]>,
73    /// End of the element name; the name starts at the start of `buf`.
74    pub(crate) name_len: usize,
75    /// Encoding used for `buf`.
76    decoder: Decoder,
77}
78
79impl<'a> BytesStart<'a> {
80    /// Internal constructor, used by the reader; supplies data in the reader's encoding.
81    #[inline]
82    #[allow(dead_code)] // used by the reader in a later pipeline stage
83    pub(crate) const fn wrap(content: &'a [u8], name_len: usize, decoder: Decoder) -> Self {
84        BytesStart {
85            buf: Cow::Borrowed(content),
86            name_len,
87            decoder,
88        }
89    }
90
91    /// Creates a new `BytesStart` from the given name.
92    ///
93    /// # Warning
94    ///
95    /// `name` must be a valid name.
96    #[inline]
97    pub fn new<C: Into<Cow<'a, str>>>(name: C) -> Self {
98        let buf = str_cow_to_bytes(name);
99        BytesStart {
100            name_len: buf.len(),
101            buf,
102            decoder: Decoder::utf8(),
103        }
104    }
105
106    /// Creates a new `BytesStart` from the given content (name + attributes).
107    ///
108    /// # Warning
109    ///
110    /// `&content[..name_len]` must be a valid name and the remainder of `content`
111    /// must be correctly-formed attributes. Neither is checked.
112    #[inline]
113    pub fn from_content<C: Into<Cow<'a, str>>>(content: C, name_len: usize) -> Self {
114        BytesStart {
115            buf: str_cow_to_bytes(content),
116            name_len,
117            decoder: Decoder::utf8(),
118        }
119    }
120
121    /// Converts the event into an owned event.
122    pub fn into_owned(self) -> BytesStart<'static> {
123        BytesStart {
124            buf: Cow::Owned(self.buf.into_owned()),
125            name_len: self.name_len,
126            decoder: self.decoder,
127        }
128    }
129
130    /// Converts the event into an owned event without taking ownership of it.
131    pub fn to_owned(&self) -> BytesStart<'static> {
132        BytesStart {
133            buf: Cow::Owned(self.buf.clone().into_owned()),
134            name_len: self.name_len,
135            decoder: self.decoder,
136        }
137    }
138
139    /// Converts the event into a borrowed event. Most useful when paired with
140    /// [`to_end`](Self::to_end).
141    pub fn borrow(&self) -> BytesStart<'_> {
142        BytesStart {
143            buf: Cow::Borrowed(&self.buf),
144            name_len: self.name_len,
145            decoder: self.decoder,
146        }
147    }
148
149    /// Creates a new paired closing tag.
150    #[inline]
151    pub fn to_end(&self) -> BytesEnd<'_> {
152        BytesEnd::from(self.name())
153    }
154
155    /// Gets the decoder used to decode the bytes of the reader that produced this
156    /// event. For manually-constructed events the encoding is UTF-8.
157    #[inline]
158    pub const fn decoder(&self) -> Decoder {
159        self.decoder
160    }
161
162    /// Gets the undecoded raw tag name, as present in the input stream.
163    #[inline]
164    pub fn name(&self) -> QName<'_> {
165        QName(&self.buf[..self.name_len])
166    }
167
168    /// Gets the undecoded raw local tag name (excluding the namespace prefix), as
169    /// present in the input stream.
170    #[inline]
171    pub fn local_name(&self) -> LocalName<'_> {
172        self.name().into()
173    }
174
175    /// Edits the name of the `BytesStart` in place.
176    ///
177    /// # Warning
178    ///
179    /// `name` must be a valid name.
180    pub fn set_name(&mut self, name: &[u8]) -> &mut BytesStart<'a> {
181        let bytes = self.buf.to_mut();
182        bytes.splice(..self.name_len, name.iter().cloned());
183        self.name_len = name.len();
184        self
185    }
186}
187
188/// Attribute-related methods.
189impl<'a> BytesStart<'a> {
190    /// Consumes `self` and yields a new `BytesStart` with additional attributes
191    /// from an iterator. Items must be convertible to [`Attribute`] via `Into`.
192    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
193    where
194        I: IntoIterator,
195        I::Item: Into<Attribute<'b>>,
196    {
197        self.extend_attributes(attributes);
198        self
199    }
200
201    /// Adds additional attributes to this tag using an iterator. Items must be
202    /// convertible to [`Attribute`] via `Into`.
203    pub fn extend_attributes<'b, I>(&mut self, attributes: I) -> &mut BytesStart<'a>
204    where
205        I: IntoIterator,
206        I::Item: Into<Attribute<'b>>,
207    {
208        for attr in attributes {
209            self.push_attribute(attr);
210        }
211        self
212    }
213
214    /// Adds an attribute to this element.
215    pub fn push_attribute<'b, A>(&mut self, attr: A)
216    where
217        A: Into<Attribute<'b>>,
218    {
219        self.buf.to_mut().push(b' ');
220        self.push_attr(attr.into());
221    }
222
223    /// Removes all attributes from this `BytesStart`.
224    pub fn clear_attributes(&mut self) -> &mut BytesStart<'a> {
225        self.buf.to_mut().truncate(self.name_len);
226        self
227    }
228
229    /// Returns an iterator over the attributes of this tag.
230    pub fn attributes(&self) -> Attributes<'_> {
231        Attributes::wrap(&self.buf, self.name_len, false, self.decoder)
232    }
233
234    /// Returns an iterator over the HTML-like attributes of this tag (no mandatory
235    /// quotes or `=`).
236    pub fn html_attributes(&self) -> Attributes<'_> {
237        Attributes::wrap(&self.buf, self.name_len, true, self.decoder)
238    }
239
240    /// Gets the undecoded raw string with the attributes of this tag, including
241    /// the white space after the tag name if there is any.
242    #[inline]
243    pub fn attributes_raw(&self) -> &[u8] {
244        &self.buf[self.name_len..]
245    }
246
247    /// Tries to get an attribute by name.
248    pub fn try_get_attribute<N: AsRef<[u8]> + Sized>(
249        &'a self,
250        attr_name: N,
251    ) -> Result<Option<Attribute<'a>>, AttrError> {
252        for a in self.attributes().with_checks(false) {
253            let a = a?;
254            if a.key.as_ref() == attr_name.as_ref() {
255                return Ok(Some(a));
256            }
257        }
258        Ok(None)
259    }
260
261    /// Adds an attribute to this element (without a leading space).
262    pub(crate) fn push_attr<'b>(&mut self, attr: Attribute<'b>) {
263        let bytes = self.buf.to_mut();
264        bytes.extend_from_slice(attr.key.as_ref());
265        bytes.extend_from_slice(b"=\"");
266        bytes.extend_from_slice(attr.value.as_ref());
267        bytes.push(b'"');
268    }
269
270    /// Adds a new line into the existing content of the element. Used by the
271    /// [`Writer`](crate::writer::Writer) when laying out attributes across lines.
272    pub(crate) fn push_newline(&mut self) {
273        self.buf.to_mut().push(b'\n');
274    }
275
276    /// Adds indentation bytes into the existing content of the element. Used by
277    /// the [`Writer`](crate::writer::Writer) when laying out attributes across lines.
278    pub(crate) fn push_indent(&mut self, indent: &[u8]) {
279        self.buf.to_mut().extend_from_slice(indent);
280    }
281}
282
283impl<'a> Debug for BytesStart<'a> {
284    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
285        write!(f, "BytesStart {{ buf: ")?;
286        write_cow_string(f, &self.buf)?;
287        write!(f, ", name_len: {} }}", self.name_len)
288    }
289}
290
291impl<'a> Deref for BytesStart<'a> {
292    type Target = [u8];
293
294    fn deref(&self) -> &[u8] {
295        &self.buf
296    }
297}
298
299/// Generates an element name (a non-empty alphanumeric string, so that it is
300/// always a syntactically valid `Name`) plus a fuzzer-chosen set of
301/// attributes, mirroring quick-xml 0.41's `Arbitrary` impl for `BytesStart`.
302#[cfg(feature = "arbitrary")]
303#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
304impl<'a> arbitrary::Arbitrary<'a> for BytesStart<'a> {
305    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
306        let s = <&str>::arbitrary(u)?;
307        if s.is_empty() || !s.chars().all(char::is_alphanumeric) {
308            return Err(arbitrary::Error::IncorrectFormat);
309        }
310        let mut result = Self::new(s);
311        result.extend_attributes(Vec::<(&str, &str)>::arbitrary(u)?);
312        Ok(result)
313    }
314
315    fn size_hint(depth: usize) -> (usize, Option<usize>) {
316        <&str as arbitrary::Arbitrary>::size_hint(depth)
317    }
318}
319
320////////////////////////////////////////////////////////////////////////////////////////////////////
321
322/// Closing tag data ([`Event::End`]): `</name>`.
323///
324/// The name can be accessed using the [`name`](Self::name) or
325/// [`local_name`](Self::local_name) methods. This event dereferences to the tag
326/// content between `</` and `>`.
327///
328/// ```
329/// # use oxixml_xml::events::BytesEnd;
330/// let event = BytesEnd::new("element");
331/// assert_eq!(event.name().as_ref(), b"element");
332/// assert_eq!(&event as &[u8], b"element");
333/// ```
334#[derive(Clone, Eq, PartialEq)]
335pub struct BytesEnd<'a> {
336    name: Cow<'a, [u8]>,
337}
338
339impl<'a> BytesEnd<'a> {
340    /// Internal constructor, used by the reader; supplies data in the reader's encoding.
341    #[inline]
342    pub(crate) const fn wrap(name: Cow<'a, [u8]>) -> Self {
343        BytesEnd { name }
344    }
345
346    /// Creates a new `BytesEnd` borrowing a slice.
347    ///
348    /// # Warning
349    ///
350    /// `name` must be a valid name.
351    #[inline]
352    pub fn new<C: Into<Cow<'a, str>>>(name: C) -> Self {
353        Self::wrap(str_cow_to_bytes(name))
354    }
355
356    /// Converts the event into an owned event.
357    pub fn into_owned(self) -> BytesEnd<'static> {
358        BytesEnd {
359            name: Cow::Owned(self.name.into_owned()),
360        }
361    }
362
363    /// Converts the event into a borrowed event.
364    #[inline]
365    pub fn borrow(&self) -> BytesEnd<'_> {
366        BytesEnd {
367            name: Cow::Borrowed(&self.name),
368        }
369    }
370
371    /// Gets the undecoded raw tag name, as present in the input stream.
372    #[inline]
373    pub fn name(&self) -> QName<'_> {
374        QName(&self.name)
375    }
376
377    /// Gets the undecoded raw local tag name (excluding the namespace prefix).
378    #[inline]
379    pub fn local_name(&self) -> LocalName<'_> {
380        self.name().into()
381    }
382}
383
384impl<'a> Debug for BytesEnd<'a> {
385    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
386        write!(f, "BytesEnd {{ name: ")?;
387        write_cow_string(f, &self.name)?;
388        write!(f, " }}")
389    }
390}
391
392impl<'a> Deref for BytesEnd<'a> {
393    type Target = [u8];
394
395    fn deref(&self) -> &[u8] {
396        &self.name
397    }
398}
399
400impl<'a> From<QName<'a>> for BytesEnd<'a> {
401    #[inline]
402    fn from(name: QName<'a>) -> Self {
403        Self::wrap(name.into_inner().into())
404    }
405}
406
407/// Mirrors quick-xml 0.41's `Arbitrary` impl for `BytesEnd`: any fuzzer-chosen
408/// string, without the non-empty/alphanumeric validation [`BytesStart`]'s impl
409/// applies (an end tag's name is not independently validated by this type).
410#[cfg(feature = "arbitrary")]
411#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
412impl<'a> arbitrary::Arbitrary<'a> for BytesEnd<'a> {
413    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
414        Ok(Self::new(<&str>::arbitrary(u)?))
415    }
416
417    fn size_hint(depth: usize) -> (usize, Option<usize>) {
418        <&str as arbitrary::Arbitrary>::size_hint(depth)
419    }
420}
421
422////////////////////////////////////////////////////////////////////////////////////////////////////
423
424/// Data from various events (most notably [`Event::Text`]).
425///
426/// This event dereferences to its content. For a comment this is everything
427/// between `<!--` and `-->`; for a DTD everything between `<!DOCTYPE` (plus
428/// spaces) and the closing `>`.
429///
430/// ```
431/// # use oxixml_xml::events::BytesText;
432/// let event = BytesText::new("comment or text ");
433/// assert_eq!(&event as &[u8], b"comment or text ");
434/// ```
435#[derive(Clone, Eq, PartialEq)]
436pub struct BytesText<'a> {
437    /// Escaped, then encoded, content of the event.
438    content: Cow<'a, [u8]>,
439    /// Encoding in which `content` is stored.
440    decoder: Decoder,
441}
442
443impl<'a> BytesText<'a> {
444    /// Creates a new `BytesText` from a raw byte sequence in the specified encoding.
445    #[inline]
446    pub(crate) fn wrap<C: Into<Cow<'a, [u8]>>>(content: C, decoder: Decoder) -> Self {
447        Self {
448            content: content.into(),
449            decoder,
450        }
451    }
452
453    /// Creates a new `BytesText` from a raw string as it appeared in the XML source.
454    ///
455    /// # Warning
456    ///
457    /// `content` is not checked to not contain markup or entity references.
458    #[inline]
459    pub fn from_escaped<C: Into<Cow<'a, str>>>(content: C) -> Self {
460        Self::wrap(str_cow_to_bytes(content), Decoder::utf8())
461    }
462
463    /// Creates a new `BytesText` from a string, escaping it using [`escape`].
464    ///
465    /// [`escape`]: crate::escape::escape
466    #[inline]
467    pub fn new(content: &'a str) -> Self {
468        Self::from_escaped(escape(content))
469    }
470
471    /// Ensures that all data is owned to extend the object's lifetime if necessary.
472    #[inline]
473    pub fn into_owned(self) -> BytesText<'static> {
474        BytesText {
475            content: self.content.into_owned().into(),
476            decoder: self.decoder,
477        }
478    }
479
480    /// Extracts the inner `Cow` from the `BytesText` event container.
481    #[inline]
482    pub fn into_inner(self) -> Cow<'a, [u8]> {
483        self.content
484    }
485
486    /// Converts the event into a borrowed event.
487    #[inline]
488    pub fn borrow(&self) -> BytesText<'_> {
489        BytesText {
490            content: Cow::Borrowed(&self.content),
491            decoder: self.decoder,
492        }
493    }
494
495    /// Decodes the content of the event.
496    ///
497    /// This does not normalize end-of-line characters; usually you need
498    /// [`xml_content`](Self::xml_content) instead.
499    pub fn decode(&self) -> Result<Cow<'a, str>, EncodingError> {
500        self.decoder.decode_cow(&self.content)
501    }
502
503    /// Decodes the content as XML 1.0 or HTML, normalizing end-of-line characters.
504    pub fn xml10_content(&self) -> Result<Cow<'a, str>, EncodingError> {
505        self.decoder.content(&self.content, normalize_xml10_eols)
506    }
507
508    /// Decodes the content as XML 1.1, normalizing end-of-line characters.
509    pub fn xml11_content(&self) -> Result<Cow<'a, str>, EncodingError> {
510        self.decoder.content(&self.content, normalize_xml11_eols)
511    }
512
513    /// Decodes the content according to the specified XML version.
514    #[inline]
515    pub fn xml_content(&self, version: XmlVersion) -> Result<Cow<'a, str>, EncodingError> {
516        match version {
517            XmlVersion::Explicit1_1 => self.xml11_content(),
518            _ => self.xml10_content(),
519        }
520    }
521
522    /// Alias for [`xml10_content`](Self::xml10_content).
523    #[inline]
524    pub fn html_content(&self) -> Result<Cow<'a, str>, EncodingError> {
525        self.xml10_content()
526    }
527
528    /// Removes leading XML white space bytes. Returns `true` if the content is
529    /// empty afterwards.
530    pub fn inplace_trim_start(&mut self) -> bool {
531        self.content = trim_cow(
532            replace(&mut self.content, Cow::Borrowed(b"")),
533            trim_xml_start,
534        );
535        self.content.is_empty()
536    }
537
538    /// Removes trailing XML white space bytes. Returns `true` if the content is
539    /// empty afterwards.
540    pub fn inplace_trim_end(&mut self) -> bool {
541        self.content = trim_cow(replace(&mut self.content, Cow::Borrowed(b"")), trim_xml_end);
542        self.content.is_empty()
543    }
544}
545
546impl<'a> Debug for BytesText<'a> {
547    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
548        write!(f, "BytesText {{ content: ")?;
549        write_cow_string(f, &self.content)?;
550        write!(f, " }}")
551    }
552}
553
554impl<'a> Deref for BytesText<'a> {
555    type Target = [u8];
556
557    fn deref(&self) -> &[u8] {
558        &self.content
559    }
560}
561
562/// Generates alphanumeric-only text content, mirroring quick-xml 0.41's
563/// `Arbitrary` impl for `BytesText`: restricting the charset sidesteps having
564/// to reason about whether the fuzzer-chosen string round-trips through
565/// [`escape`](Self::new)/unescape unchanged.
566#[cfg(feature = "arbitrary")]
567#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
568impl<'a> arbitrary::Arbitrary<'a> for BytesText<'a> {
569    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
570        let s = <&str>::arbitrary(u)?;
571        if !s.chars().all(char::is_alphanumeric) {
572            return Err(arbitrary::Error::IncorrectFormat);
573        }
574        Ok(Self::new(s))
575    }
576
577    fn size_hint(depth: usize) -> (usize, Option<usize>) {
578        <&str as arbitrary::Arbitrary>::size_hint(depth)
579    }
580}
581
582////////////////////////////////////////////////////////////////////////////////////////////////////
583
584/// CDATA content ([`Event::CData`]) which contains unescaped data. To write it as
585/// text, [convert](Self::escape) it to [`BytesText`].
586///
587/// This event dereferences to its content between `<![CDATA[` and `]]>`.
588///
589/// ```
590/// # use oxixml_xml::events::BytesCData;
591/// let event = BytesCData::new(" CDATA section ");
592/// assert_eq!(&event as &[u8], b" CDATA section ");
593/// ```
594#[derive(Clone, Eq, PartialEq)]
595pub struct BytesCData<'a> {
596    content: Cow<'a, [u8]>,
597    /// Encoding in which `content` is stored.
598    decoder: Decoder,
599}
600
601impl<'a> BytesCData<'a> {
602    /// Creates a new `BytesCData` from a byte sequence in the specified encoding.
603    #[inline]
604    pub(crate) fn wrap<C: Into<Cow<'a, [u8]>>>(content: C, decoder: Decoder) -> Self {
605        Self {
606            content: content.into(),
607            decoder,
608        }
609    }
610
611    /// Creates a new `BytesCData` from a string.
612    ///
613    /// # Warning
614    ///
615    /// `content` must not contain the `]]>` sequence. Use [`BytesCData::escaped`]
616    /// to split content that might.
617    #[inline]
618    pub fn new<C: Into<Cow<'a, str>>>(content: C) -> Self {
619        Self::wrap(str_cow_to_bytes(content), Decoder::utf8())
620    }
621
622    /// Creates an iterator of `BytesCData` from a string, splitting it into
623    /// multiple `CDATA` sections wherever the `]]>` sequence appears (because it
624    /// cannot be escaped).
625    ///
626    /// ```
627    /// # use oxixml_xml::events::BytesCData;
628    /// let content = "Certain tokens like ]]> can be difficult and <invalid>";
629    /// let cdata = BytesCData::escaped(content).collect::<Vec<_>>();
630    /// assert_eq!(cdata, &[
631    ///     BytesCData::new("Certain tokens like ]]"),
632    ///     BytesCData::new("> can be difficult and <invalid>"),
633    /// ]);
634    /// ```
635    #[inline]
636    pub const fn escaped(content: &'a str) -> CDataIterator<'a> {
637        CDataIterator {
638            inner: utils::CDataIterator::new(content),
639        }
640    }
641
642    /// Ensures that all data is owned to extend the object's lifetime if necessary.
643    #[inline]
644    pub fn into_owned(self) -> BytesCData<'static> {
645        BytesCData {
646            content: self.content.into_owned().into(),
647            decoder: self.decoder,
648        }
649    }
650
651    /// Extracts the inner `Cow` from the `BytesCData` event container.
652    #[inline]
653    pub fn into_inner(self) -> Cow<'a, [u8]> {
654        self.content
655    }
656
657    /// Converts the event into a borrowed event.
658    #[inline]
659    pub fn borrow(&self) -> BytesCData<'_> {
660        BytesCData {
661            content: Cow::Borrowed(&self.content),
662            decoder: self.decoder,
663        }
664    }
665
666    /// Converts this CDATA content into an escaped [`BytesText`] (escaping `<`,
667    /// `>`, `&`, `'` and `"`).
668    pub fn escape(self) -> Result<BytesText<'a>, EncodingError> {
669        let decoded = self.decode()?;
670        Ok(BytesText::wrap(
671            match escape(decoded) {
672                Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
673                Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
674            },
675            Decoder::utf8(),
676        ))
677    }
678
679    /// Converts this CDATA content into a partially-escaped [`BytesText`]
680    /// (escaping `<`, `>` and `&`).
681    pub fn partial_escape(self) -> Result<BytesText<'a>, EncodingError> {
682        let decoded = self.decode()?;
683        Ok(BytesText::wrap(
684            match partial_escape(decoded) {
685                Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
686                Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
687            },
688            Decoder::utf8(),
689        ))
690    }
691
692    /// Converts this CDATA content into a minimally-escaped [`BytesText`]
693    /// (escaping only `<` and `&`).
694    pub fn minimal_escape(self) -> Result<BytesText<'a>, EncodingError> {
695        let decoded = self.decode()?;
696        Ok(BytesText::wrap(
697            match minimal_escape(decoded) {
698                Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
699                Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
700            },
701            Decoder::utf8(),
702        ))
703    }
704
705    /// Decodes the raw byte content of the CDATA section, without XML entity
706    /// escaping and without EOL normalization.
707    pub fn decode(&self) -> Result<Cow<'a, str>, EncodingError> {
708        self.decoder.decode_cow(&self.content)
709    }
710
711    /// Decodes the CDATA content as XML 1.0 or HTML, normalizing EOLs.
712    pub fn xml10_content(&self) -> Result<Cow<'a, str>, EncodingError> {
713        self.decoder.content(&self.content, normalize_xml10_eols)
714    }
715
716    /// Decodes the CDATA content as XML 1.1, normalizing EOLs.
717    pub fn xml11_content(&self) -> Result<Cow<'a, str>, EncodingError> {
718        self.decoder.content(&self.content, normalize_xml11_eols)
719    }
720
721    /// Decodes the CDATA content according to the specified XML version.
722    #[inline]
723    pub fn xml_content(&self, version: XmlVersion) -> Result<Cow<'a, str>, EncodingError> {
724        match version {
725            XmlVersion::Explicit1_1 => self.xml11_content(),
726            _ => self.xml10_content(),
727        }
728    }
729
730    /// Alias for [`xml10_content`](Self::xml10_content).
731    #[inline]
732    pub fn html_content(&self) -> Result<Cow<'a, str>, EncodingError> {
733        self.xml10_content()
734    }
735}
736
737impl<'a> Debug for BytesCData<'a> {
738    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
739        write!(f, "BytesCData {{ content: ")?;
740        write_cow_string(f, &self.content)?;
741        write!(f, " }}")
742    }
743}
744
745impl<'a> Deref for BytesCData<'a> {
746    type Target = [u8];
747
748    fn deref(&self) -> &[u8] {
749        &self.content
750    }
751}
752
753/// Mirrors quick-xml 0.41's `Arbitrary` impl for `BytesCData`: any
754/// fuzzer-chosen string, unvalidated (unlike `BytesCData::new`, this does not
755/// require the absence of `]]>`).
756#[cfg(feature = "arbitrary")]
757#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
758impl<'a> arbitrary::Arbitrary<'a> for BytesCData<'a> {
759    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
760        Ok(Self::new(<&str>::arbitrary(u)?))
761    }
762
763    fn size_hint(depth: usize) -> (usize, Option<usize>) {
764        <&str as arbitrary::Arbitrary>::size_hint(depth)
765    }
766}
767
768/// Iterator over `CDATA` sections in a string, created by [`BytesCData::escaped`].
769#[derive(Debug, Clone)]
770pub struct CDataIterator<'a> {
771    inner: utils::CDataIterator<'a>,
772}
773
774impl<'a> Iterator for CDataIterator<'a> {
775    type Item = BytesCData<'a>;
776
777    fn next(&mut self) -> Option<BytesCData<'a>> {
778        self.inner
779            .next()
780            .map(|slice| BytesCData::wrap(slice.as_bytes(), Decoder::utf8()))
781    }
782}
783
784impl FusedIterator for CDataIterator<'_> {}
785
786////////////////////////////////////////////////////////////////////////////////////////////////////
787
788/// [Processing instructions][pi] (PIs), allowing documents to carry instructions
789/// for applications ([`Event::PI`]).
790///
791/// This event dereferences to its content between `<?` and `?>`.
792///
793/// ```
794/// # use oxixml_xml::events::BytesPI;
795/// let instruction = BytesPI::new(r#"xml-stylesheet href="style.css""#);
796/// assert_eq!(instruction.target(), b"xml-stylesheet");
797/// assert_eq!(instruction.content(), br#" href="style.css""#);
798/// ```
799///
800/// [pi]: https://www.w3.org/TR/xml11/#sec-pi
801#[derive(Clone, Eq, PartialEq)]
802pub struct BytesPI<'a> {
803    content: BytesStart<'a>,
804}
805
806impl<'a> BytesPI<'a> {
807    /// Creates a new `BytesPI` from a byte sequence in the specified encoding.
808    #[inline]
809    #[allow(dead_code)] // used by the reader in a later pipeline stage
810    pub(crate) const fn wrap(content: &'a [u8], target_len: usize, decoder: Decoder) -> Self {
811        Self {
812            content: BytesStart::wrap(content, target_len, decoder),
813        }
814    }
815
816    /// Creates a new `BytesPI` from a string.
817    ///
818    /// # Warning
819    ///
820    /// `content` must not contain the `?>` sequence.
821    #[inline]
822    pub fn new<C: Into<Cow<'a, str>>>(content: C) -> Self {
823        let buf = str_cow_to_bytes(content);
824        let name_len = name_len(&buf);
825        Self {
826            content: BytesStart {
827                buf,
828                name_len,
829                decoder: Decoder::utf8(),
830            },
831        }
832    }
833
834    /// Ensures that all data is owned to extend the object's lifetime if necessary.
835    #[inline]
836    pub fn into_owned(self) -> BytesPI<'static> {
837        BytesPI {
838            content: self.content.into_owned(),
839        }
840    }
841
842    /// Extracts the inner `Cow` from the `BytesPI` event container.
843    #[inline]
844    pub fn into_inner(self) -> Cow<'a, [u8]> {
845        self.content.buf
846    }
847
848    /// Converts the event into a borrowed event.
849    #[inline]
850    pub fn borrow(&self) -> BytesPI<'_> {
851        BytesPI {
852            content: self.content.borrow(),
853        }
854    }
855
856    /// The target that identifies the application to which the instruction is
857    /// directed.
858    #[inline]
859    pub fn target(&self) -> &[u8] {
860        self.content.name().0
861    }
862
863    /// The content of the processing instruction: everything after the target
864    /// name. The first character is therefore always a space.
865    #[inline]
866    pub fn content(&self) -> &[u8] {
867        self.content.attributes_raw()
868    }
869
870    /// A view of the processing instruction's content as a list of key-value pairs.
871    ///
872    /// ```
873    /// # use std::borrow::Cow;
874    /// # use oxixml_xml::events::attributes::Attribute;
875    /// # use oxixml_xml::events::BytesPI;
876    /// # use oxixml_xml::name::QName;
877    /// let instruction = BytesPI::new(r#"xml-stylesheet href="style.css""#);
878    /// for attr in instruction.attributes() {
879    ///     assert_eq!(attr, Ok(Attribute {
880    ///         key: QName(b"href"),
881    ///         value: Cow::Borrowed(b"style.css"),
882    ///     }));
883    /// }
884    /// ```
885    #[inline]
886    pub fn attributes(&self) -> Attributes<'_> {
887        self.content.attributes()
888    }
889}
890
891impl<'a> Debug for BytesPI<'a> {
892    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
893        write!(f, "BytesPI {{ content: ")?;
894        write_cow_string(f, &self.content.buf)?;
895        write!(f, " }}")
896    }
897}
898
899impl<'a> Deref for BytesPI<'a> {
900    type Target = [u8];
901
902    fn deref(&self) -> &[u8] {
903        &self.content
904    }
905}
906
907/// Mirrors quick-xml 0.41's `Arbitrary` impl for `BytesPI`: any fuzzer-chosen
908/// string, unvalidated (unlike `BytesPI::new`, this does not require the
909/// absence of `?>`).
910#[cfg(feature = "arbitrary")]
911#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
912impl<'a> arbitrary::Arbitrary<'a> for BytesPI<'a> {
913    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
914        Ok(Self::new(<&str>::arbitrary(u)?))
915    }
916
917    fn size_hint(depth: usize) -> (usize, Option<usize>) {
918        <&str as arbitrary::Arbitrary>::size_hint(depth)
919    }
920}
921
922////////////////////////////////////////////////////////////////////////////////////////////////////
923
924/// An XML declaration ([`Event::Decl`]): `<?xml version="1.0" ...?>`.
925///
926/// This event dereferences to its content between `<?` and `?>`.
927///
928/// ```
929/// # use oxixml_xml::events::{BytesDecl, BytesStart};
930/// let content = "xml version = '1.0' ";
931/// let event = BytesDecl::from_start(BytesStart::from_content(content, 3));
932/// assert_eq!(&event as &[u8], content.as_bytes());
933/// ```
934#[derive(Clone, Debug, Eq, PartialEq)]
935pub struct BytesDecl<'a> {
936    content: BytesStart<'a>,
937}
938
939impl<'a> BytesDecl<'a> {
940    /// Constructs a new declaration from the mandatory `version` (should be `1.0`
941    /// or `1.1`), the optional `encoding` and the optional `standalone` attribute.
942    ///
943    /// Inputs are not escaped and attribute values are always wrapped in double
944    /// quotes.
945    pub fn new(
946        version: &str,
947        encoding: Option<&str>,
948        standalone: Option<&str>,
949    ) -> BytesDecl<'static> {
950        // ' encoding=""'   => 12
951        let encoding_attr_len = if let Some(xs) = encoding {
952            12 + xs.len()
953        } else {
954            0
955        };
956        // ' standalone=""' => 14
957        let standalone_attr_len = if let Some(xs) = standalone {
958            14 + xs.len()
959        } else {
960            0
961        };
962        // 'xml version=""' => 14
963        let mut buf = String::with_capacity(14 + encoding_attr_len + standalone_attr_len);
964
965        buf.push_str("xml version=\"");
966        buf.push_str(version);
967
968        if let Some(encoding_val) = encoding {
969            buf.push_str("\" encoding=\"");
970            buf.push_str(encoding_val);
971        }
972
973        if let Some(standalone_val) = standalone {
974            buf.push_str("\" standalone=\"");
975            buf.push_str(standalone_val);
976        }
977        buf.push('"');
978
979        BytesDecl {
980            content: BytesStart::from_content(buf, 3),
981        }
982    }
983
984    /// Creates a `BytesDecl` from a [`BytesStart`].
985    pub const fn from_start(start: BytesStart<'a>) -> Self {
986        Self { content: start }
987    }
988
989    /// Gets the XML version, excluding quotes.
990    ///
991    /// The version *must* be the first attribute in the declaration. If it is
992    /// missing or not first, [`IllFormedError::MissingDeclVersion`] is returned.
993    ///
994    /// ```
995    /// # use oxixml_xml::errors::{Error, IllFormedError};
996    /// # use oxixml_xml::events::{BytesDecl, BytesStart};
997    /// let decl = BytesDecl::from_start(BytesStart::from_content(" version='1.1'", 0));
998    /// assert_eq!(decl.version().expect("doc example"), b"1.1".as_ref());
999    ///
1000    /// let decl = BytesDecl::from_start(BytesStart::from_content(" encoding='utf-8'", 0));
1001    /// match decl.version() {
1002    ///     Err(Error::IllFormed(IllFormedError::MissingDeclVersion(Some(key)))) => assert_eq!(key, "encoding"),
1003    ///     _ => panic!("expected MissingDeclVersion"),
1004    /// }
1005    /// ```
1006    pub fn version(&self) -> Result<Cow<'_, [u8]>, Error> {
1007        match self.content.attributes().with_checks(false).next() {
1008            Some(Ok(a)) if a.key.as_ref() == b"version" => Ok(a.value),
1009            Some(Ok(a)) => {
1010                let found = from_utf8(a.key.as_ref())
1011                    .map_err(|_| IllFormedError::MissingDeclVersion(None))?
1012                    .to_string();
1013                Err(Error::IllFormed(IllFormedError::MissingDeclVersion(Some(
1014                    found,
1015                ))))
1016            }
1017            Some(Err(e)) => Err(e.into()),
1018            None => Err(Error::IllFormed(IllFormedError::MissingDeclVersion(None))),
1019        }
1020    }
1021
1022    /// Gets the XML encoding, excluding quotes. Returns the first occurrence even
1023    /// if there are several; the name is not restricted to the grammar.
1024    pub fn encoding(&self) -> Option<Result<Cow<'_, [u8]>, AttrError>> {
1025        self.content
1026            .try_get_attribute("encoding")
1027            .map(|a| a.map(|a| a.value))
1028            .transpose()
1029    }
1030
1031    /// Gets the XML `standalone` flag, excluding quotes. Returns the first
1032    /// occurrence even if there are several.
1033    pub fn standalone(&self) -> Option<Result<Cow<'_, [u8]>, AttrError>> {
1034        self.content
1035            .try_get_attribute("standalone")
1036            .map(|a| a.map(|a| a.value))
1037            .transpose()
1038    }
1039
1040    /// Gets the XML version as a typed enumeration.
1041    ///
1042    /// Returns [`IllFormedError::MissingDeclVersion`] if the version is missing or
1043    /// not first, and [`IllFormedError::UnknownVersion`] if it is neither 1.0 nor 1.1.
1044    pub fn xml_version(&self) -> Result<XmlVersion, Error> {
1045        let v = self.version()?;
1046        match v.as_ref() {
1047            b"1.0" => Ok(XmlVersion::Explicit1_0),
1048            b"1.1" => Ok(XmlVersion::Explicit1_1),
1049            _ => Err(Error::IllFormed(IllFormedError::UnknownVersion)),
1050        }
1051    }
1052
1053    /// Resolves the declared encoding name to an [`Encoding`] using the
1054    /// [_get an encoding_](https://encoding.spec.whatwg.org/#concept-encoding-get)
1055    /// algorithm. Returns `None` if the encoding is unknown or absent.
1056    ///
1057    /// This is a *context-free* label lookup: a bare `UTF-16` label carries no
1058    /// endianness of its own (XML 1.0 Appendix F, RFC 2781), so per the WHATWG
1059    /// algorithm this resolves it to [`Encoding::Utf16Le`] as an arbitrary
1060    /// default. Do not treat that default as authoritative over a byte-order
1061    /// mark or the XML 1.0 Appendix F.1 four-byte pattern already observed for
1062    /// the input — [`Reader`](crate::reader::Reader) does not: it keeps
1063    /// whichever UTF-16 byte order the bytes themselves established, using
1064    /// this method only to confirm the encoding *family*, not its byte order.
1065    #[cfg(feature = "encoding")]
1066    #[cfg_attr(docsrs, doc(cfg(feature = "encoding")))]
1067    pub fn encoder(&self) -> Option<Encoding> {
1068        self.encoding()
1069            .and_then(|e| e.ok())
1070            .and_then(|e| std::str::from_utf8(&e).ok().and_then(Encoding::for_label))
1071    }
1072
1073    /// Converts the event into an owned event.
1074    pub fn into_owned(self) -> BytesDecl<'static> {
1075        BytesDecl {
1076            content: self.content.into_owned(),
1077        }
1078    }
1079
1080    /// Converts the event into a borrowed event.
1081    #[inline]
1082    pub fn borrow(&self) -> BytesDecl<'_> {
1083        BytesDecl {
1084            content: self.content.borrow(),
1085        }
1086    }
1087}
1088
1089impl<'a> Deref for BytesDecl<'a> {
1090    type Target = [u8];
1091
1092    fn deref(&self) -> &[u8] {
1093        &self.content
1094    }
1095}
1096
1097/// Mirrors quick-xml 0.41's `Arbitrary` impl for `BytesDecl`: fuzzer-chosen
1098/// `version`, `encoding` and `standalone` values assembled through
1099/// [`BytesDecl::new`], unvalidated.
1100#[cfg(feature = "arbitrary")]
1101#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
1102impl<'a> arbitrary::Arbitrary<'a> for BytesDecl<'a> {
1103    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1104        Ok(Self::new(
1105            <&str>::arbitrary(u)?,
1106            Option::<&str>::arbitrary(u)?,
1107            Option::<&str>::arbitrary(u)?,
1108        ))
1109    }
1110
1111    fn size_hint(depth: usize) -> (usize, Option<usize>) {
1112        <&str as arbitrary::Arbitrary>::size_hint(depth)
1113    }
1114}
1115
1116////////////////////////////////////////////////////////////////////////////////////////////////////
1117
1118/// A character or general entity reference ([`Event::GeneralRef`]): `&ref;` or
1119/// `&#<number>;`.
1120///
1121/// This event dereferences to its content between `&` and `;`.
1122///
1123/// ```
1124/// # use oxixml_xml::events::BytesRef;
1125/// let event = BytesRef::new("entity");
1126/// assert_eq!(&event as &[u8], b"entity");
1127/// ```
1128#[derive(Clone, Eq, PartialEq)]
1129pub struct BytesRef<'a> {
1130    content: Cow<'a, [u8]>,
1131    /// Encoding in which `content` is stored.
1132    decoder: Decoder,
1133}
1134
1135impl<'a> BytesRef<'a> {
1136    /// Internal constructor, used by the reader; supplies data in the reader's encoding.
1137    #[inline]
1138    #[allow(dead_code)] // used by the reader in a later pipeline stage
1139    pub(crate) const fn wrap(content: &'a [u8], decoder: Decoder) -> Self {
1140        Self {
1141            content: Cow::Borrowed(content),
1142            decoder,
1143        }
1144    }
1145
1146    /// Creates a new `BytesRef` borrowing a slice.
1147    ///
1148    /// # Warning
1149    ///
1150    /// `name` must be a valid reference body.
1151    #[inline]
1152    pub fn new<C: Into<Cow<'a, str>>>(name: C) -> Self {
1153        Self {
1154            content: str_cow_to_bytes(name),
1155            decoder: Decoder::utf8(),
1156        }
1157    }
1158
1159    /// Converts the event into an owned event.
1160    pub fn into_owned(self) -> BytesRef<'static> {
1161        BytesRef {
1162            content: Cow::Owned(self.content.into_owned()),
1163            decoder: self.decoder,
1164        }
1165    }
1166
1167    /// Extracts the inner `Cow` from the `BytesRef` event container.
1168    #[inline]
1169    pub fn into_inner(self) -> Cow<'a, [u8]> {
1170        self.content
1171    }
1172
1173    /// Converts the event into a borrowed event.
1174    #[inline]
1175    pub fn borrow(&self) -> BytesRef<'_> {
1176        BytesRef {
1177            content: Cow::Borrowed(&self.content),
1178            decoder: self.decoder,
1179        }
1180    }
1181
1182    /// Decodes the content of the event without EOL normalization.
1183    pub fn decode(&self) -> Result<Cow<'a, str>, EncodingError> {
1184        self.decoder.decode_cow(&self.content)
1185    }
1186
1187    /// Decodes the content as XML 1.0 or HTML, normalizing EOLs.
1188    pub fn xml10_content(&self) -> Result<Cow<'a, str>, EncodingError> {
1189        self.decoder.content(&self.content, normalize_xml10_eols)
1190    }
1191
1192    /// Decodes the content as XML 1.1, normalizing EOLs.
1193    pub fn xml11_content(&self) -> Result<Cow<'a, str>, EncodingError> {
1194        self.decoder.content(&self.content, normalize_xml11_eols)
1195    }
1196
1197    /// Decodes the content according to the specified XML version.
1198    #[inline]
1199    pub fn xml_content(&self, version: XmlVersion) -> Result<Cow<'a, str>, EncodingError> {
1200        match version {
1201            XmlVersion::Explicit1_1 => self.xml11_content(),
1202            _ => self.xml10_content(),
1203        }
1204    }
1205
1206    /// Alias for [`xml10_content`](Self::xml10_content).
1207    #[inline]
1208    pub fn html_content(&self) -> Result<Cow<'a, str>, EncodingError> {
1209        self.xml10_content()
1210    }
1211
1212    /// Returns `true` if this reference is a character reference (`&#<number>;`).
1213    ///
1214    /// ```
1215    /// # use oxixml_xml::events::BytesRef;
1216    /// assert_eq!(BytesRef::new("#x30").is_char_ref(), true);
1217    /// assert_eq!(BytesRef::new("#49" ).is_char_ref(), true);
1218    /// assert_eq!(BytesRef::new("lt"  ).is_char_ref(), false);
1219    /// ```
1220    pub fn is_char_ref(&self) -> bool {
1221        matches!(self.content.first(), Some(b'#'))
1222    }
1223
1224    /// If this reference is a character reference, resolves and returns the
1225    /// character; otherwise returns `None`.
1226    ///
1227    /// ```
1228    /// # use oxixml_xml::events::BytesRef;
1229    /// assert_eq!(BytesRef::new("#x30").resolve_char_ref().expect("doc example"), Some('0'));
1230    /// assert_eq!(BytesRef::new("#49" ).resolve_char_ref().expect("doc example"), Some('1'));
1231    /// assert_eq!(BytesRef::new("lt"  ).resolve_char_ref().expect("doc example"), None);
1232    /// ```
1233    pub fn resolve_char_ref(&self) -> Result<Option<char>, Error> {
1234        if let Some(num) = self.decode()?.strip_prefix('#') {
1235            let ch = parse_number(num).map_err(EscapeError::InvalidCharRef)?;
1236            return Ok(Some(ch));
1237        }
1238        Ok(None)
1239    }
1240}
1241
1242impl<'a> Debug for BytesRef<'a> {
1243    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1244        write!(f, "BytesRef {{ content: ")?;
1245        write_cow_string(f, &self.content)?;
1246        write!(f, " }}")
1247    }
1248}
1249
1250impl<'a> Deref for BytesRef<'a> {
1251    type Target = [u8];
1252
1253    fn deref(&self) -> &[u8] {
1254        &self.content
1255    }
1256}
1257
1258/// Mirrors quick-xml 0.41's `Arbitrary` impl for `BytesRef`: any fuzzer-chosen
1259/// string, unvalidated (unlike `BytesRef::new`, this does not require a valid
1260/// reference body).
1261#[cfg(feature = "arbitrary")]
1262#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
1263impl<'a> arbitrary::Arbitrary<'a> for BytesRef<'a> {
1264    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1265        Ok(Self::new(<&str>::arbitrary(u)?))
1266    }
1267
1268    fn size_hint(depth: usize) -> (usize, Option<usize>) {
1269        <&str as arbitrary::Arbitrary>::size_hint(depth)
1270    }
1271}
1272
1273////////////////////////////////////////////////////////////////////////////////////////////////////
1274
1275/// An event produced while reading (or to be written).
1276#[derive(Clone, Debug, Eq, PartialEq)]
1277#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1278pub enum Event<'a> {
1279    /// Start tag (with attributes): `<tag attr="value">`.
1280    Start(BytesStart<'a>),
1281    /// End tag: `</tag>`.
1282    End(BytesEnd<'a>),
1283    /// Empty element tag (with attributes): `<tag attr="value" />`.
1284    Empty(BytesStart<'a>),
1285    /// Escaped character data between tags.
1286    Text(BytesText<'a>),
1287    /// Unescaped character data stored in `<![CDATA[...]]>`.
1288    CData(BytesCData<'a>),
1289    /// Comment: `<!-- ... -->`.
1290    Comment(BytesText<'a>),
1291    /// XML declaration: `<?xml ...?>`.
1292    Decl(BytesDecl<'a>),
1293    /// Processing instruction: `<?...?>`.
1294    PI(BytesPI<'a>),
1295    /// Document type definition data (DTD) stored in `<!DOCTYPE ...>`.
1296    DocType(BytesText<'a>),
1297    /// A general reference `&entity;` in the textual data. Can be either an entity
1298    /// reference or a character reference.
1299    GeneralRef(BytesRef<'a>),
1300    /// End of the XML document.
1301    Eof,
1302}
1303
1304impl<'a> Event<'a> {
1305    /// Converts the event to an owned version, untied to the lifetime of the
1306    /// buffer used when reading but incurring a new, separate allocation.
1307    pub fn into_owned(self) -> Event<'static> {
1308        match self {
1309            Event::Start(e) => Event::Start(e.into_owned()),
1310            Event::End(e) => Event::End(e.into_owned()),
1311            Event::Empty(e) => Event::Empty(e.into_owned()),
1312            Event::Text(e) => Event::Text(e.into_owned()),
1313            Event::Comment(e) => Event::Comment(e.into_owned()),
1314            Event::CData(e) => Event::CData(e.into_owned()),
1315            Event::Decl(e) => Event::Decl(e.into_owned()),
1316            Event::PI(e) => Event::PI(e.into_owned()),
1317            Event::DocType(e) => Event::DocType(e.into_owned()),
1318            Event::GeneralRef(e) => Event::GeneralRef(e.into_owned()),
1319            Event::Eof => Event::Eof,
1320        }
1321    }
1322
1323    /// Converts the event into a borrowed event.
1324    #[inline]
1325    pub fn borrow(&self) -> Event<'_> {
1326        match self {
1327            Event::Start(e) => Event::Start(e.borrow()),
1328            Event::End(e) => Event::End(e.borrow()),
1329            Event::Empty(e) => Event::Empty(e.borrow()),
1330            Event::Text(e) => Event::Text(e.borrow()),
1331            Event::Comment(e) => Event::Comment(e.borrow()),
1332            Event::CData(e) => Event::CData(e.borrow()),
1333            Event::Decl(e) => Event::Decl(e.borrow()),
1334            Event::PI(e) => Event::PI(e.borrow()),
1335            Event::DocType(e) => Event::DocType(e.borrow()),
1336            Event::GeneralRef(e) => Event::GeneralRef(e.borrow()),
1337            Event::Eof => Event::Eof,
1338        }
1339    }
1340}
1341
1342impl<'a> Deref for Event<'a> {
1343    type Target = [u8];
1344
1345    fn deref(&self) -> &[u8] {
1346        match *self {
1347            Event::Start(ref e) | Event::Empty(ref e) => e,
1348            Event::End(ref e) => e,
1349            Event::Text(ref e) => e,
1350            Event::Decl(ref e) => e,
1351            Event::PI(ref e) => e,
1352            Event::CData(ref e) => e,
1353            Event::Comment(ref e) => e,
1354            Event::DocType(ref e) => e,
1355            Event::GeneralRef(ref e) => e,
1356            Event::Eof => &[],
1357        }
1358    }
1359}
1360
1361impl<'a> AsRef<Event<'a>> for Event<'a> {
1362    fn as_ref(&self) -> &Event<'a> {
1363        self
1364    }
1365}
1366
1367////////////////////////////////////////////////////////////////////////////////////////////////////
1368
1369#[inline]
1370fn str_cow_to_bytes<'a, C: Into<Cow<'a, str>>>(content: C) -> Cow<'a, [u8]> {
1371    match content.into() {
1372        Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
1373        Cow::Owned(s) => Cow::Owned(s.into_bytes()),
1374    }
1375}
1376
1377fn trim_cow<'a, F>(value: Cow<'a, [u8]>, trim: F) -> Cow<'a, [u8]>
1378where
1379    F: FnOnce(&[u8]) -> &[u8],
1380{
1381    match value {
1382        Cow::Borrowed(bytes) => Cow::Borrowed(trim(bytes)),
1383        Cow::Owned(mut bytes) => {
1384            let trimmed = trim(&bytes);
1385            if trimmed.len() != bytes.len() {
1386                bytes = trimmed.to_vec();
1387            }
1388            Cow::Owned(bytes)
1389        }
1390    }
1391}
1392
1393#[cfg(test)]
1394mod tests;