Skip to main content

oxixml_xml/de/
event.rs

1//! Simplified events consumed by the deserializer and the intermediate reader
2//! that produces them.
3
4use std::borrow::Cow;
5use std::mem::replace;
6use std::ops::{Deref, Range};
7
8use crate::de::resolver::{EntityResolver, PredefinedEntityResolver};
9use crate::de::xml_read::XmlRead;
10use crate::encoding::Decoder;
11use crate::errors::serialize::DeError;
12use crate::escape::{EscapeError, parse_number};
13use crate::events::{BytesCData, BytesEnd, BytesRef, BytesStart, BytesText};
14use crate::name::QName;
15
16/// A function to check whether the character is a whitespace (blank, new line, carriage return or tab).
17#[inline]
18const fn is_non_whitespace(ch: char) -> bool {
19    !matches!(ch, ' ' | '\r' | '\n' | '\t')
20}
21
22/// Decoded and concatenated content of consequent [`Text`] and [`CData`]
23/// events. _Consequent_ means that events should follow each other or be
24/// delimited only by (any count of) [`Comment`] or [`PI`] events.
25///
26/// Internally text is stored in `Cow<str>`. Cloning of text is cheap while it
27/// is borrowed and makes copies of data when it is owned.
28///
29/// [`Text`]: crate::events::Event::Text
30/// [`CData`]: crate::events::Event::CData
31/// [`Comment`]: crate::events::Event::Comment
32/// [`PI`]: crate::events::Event::PI
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct Text<'a> {
35    /// Untrimmed text after concatenating content of all
36    /// [`Text`] and [`CData`] events
37    ///
38    /// [`Text`]: crate::events::Event::Text
39    /// [`CData`]: crate::events::Event::CData
40    pub(crate) text: Cow<'a, str>,
41    /// A range into `text` which contains data after trimming
42    content: Range<usize>,
43}
44
45impl<'a> Text<'a> {
46    pub(crate) fn new(text: Cow<'a, str>) -> Self {
47        let start = text.find(is_non_whitespace).unwrap_or(0);
48        let end = text.rfind(is_non_whitespace).map_or(0, |i| i + 1);
49
50        let content = if start >= end { 0..0 } else { start..end };
51
52        Self { text, content }
53    }
54
55    /// Returns text without leading and trailing whitespaces as [defined] by XML specification.
56    ///
57    /// If you want to only check if text contains only whitespaces, use [`is_blank`](Self::is_blank),
58    /// which will not allocate.
59    ///
60    /// # Example
61    ///
62    /// ```
63    /// # use oxixml_xml::de::Text;
64    /// #
65    /// let text = Text::from("");
66    /// assert_eq!(text.trimmed(), "");
67    ///
68    /// let text = Text::from(" \r\n\t ");
69    /// assert_eq!(text.trimmed(), "");
70    ///
71    /// let text = Text::from("  some useful text  ");
72    /// assert_eq!(text.trimmed(), "some useful text");
73    /// ```
74    ///
75    /// [defined]: https://www.w3.org/TR/xml11/#NT-S
76    pub fn trimmed(&self) -> Cow<'a, str> {
77        match self.text {
78            Cow::Borrowed(text) => Cow::Borrowed(&text[self.content.clone()]),
79            Cow::Owned(ref text) => Cow::Owned(text[self.content.clone()].to_string()),
80        }
81    }
82
83    /// Returns `true` if text is empty or contains only whitespaces as [defined] by XML specification.
84    ///
85    /// # Example
86    ///
87    /// ```
88    /// # use oxixml_xml::de::Text;
89    /// #
90    /// let text = Text::from("");
91    /// assert_eq!(text.is_blank(), true);
92    ///
93    /// let text = Text::from(" \r\n\t ");
94    /// assert_eq!(text.is_blank(), true);
95    ///
96    /// let text = Text::from("  some useful text  ");
97    /// assert_eq!(text.is_blank(), false);
98    /// ```
99    ///
100    /// [defined]: https://www.w3.org/TR/xml11/#NT-S
101    pub fn is_blank(&self) -> bool {
102        self.content.is_empty()
103    }
104}
105
106impl<'a> Deref for Text<'a> {
107    type Target = str;
108
109    #[inline]
110    fn deref(&self) -> &Self::Target {
111        self.text.deref()
112    }
113}
114
115impl<'a> From<&'a str> for Text<'a> {
116    #[inline]
117    fn from(text: &'a str) -> Self {
118        Self::new(Cow::Borrowed(text))
119    }
120}
121
122impl<'a> From<String> for Text<'a> {
123    #[inline]
124    fn from(text: String) -> Self {
125        Self::new(Cow::Owned(text))
126    }
127}
128
129impl<'a> From<Cow<'a, str>> for Text<'a> {
130    #[inline]
131    fn from(text: Cow<'a, str>) -> Self {
132        Self::new(text)
133    }
134}
135
136////////////////////////////////////////////////////////////////////////////////////////////////////
137
138/// Simplified event which contains only these variants that used by deserializer
139#[derive(Clone, Debug, PartialEq, Eq)]
140pub enum DeEvent<'a> {
141    /// Start tag (with attributes) `<tag attr="value">`.
142    Start(BytesStart<'a>),
143    /// End tag `</tag>`.
144    End(BytesEnd<'a>),
145    /// Decoded and concatenated content of consequent [`Text`] and [`CData`]
146    /// events. _Consequent_ means that events should follow each other or be
147    /// delimited only by (any count of) [`Comment`] or [`PI`] events.
148    ///
149    /// [`Text`]: crate::events::Event::Text
150    /// [`CData`]: crate::events::Event::CData
151    /// [`Comment`]: crate::events::Event::Comment
152    /// [`PI`]: crate::events::Event::PI
153    Text(Text<'a>),
154    /// End of XML document.
155    Eof,
156}
157
158////////////////////////////////////////////////////////////////////////////////////////////////////
159
160/// Simplified event which contains only these variants that used by deserializer,
161/// but [`Text`] events not yet fully processed.
162///
163/// [`Text`] events should be trimmed if they does not surrounded by the other
164/// [`Text`] or [`CData`] events. This event contains intermediate state of [`Text`]
165/// event, where they are trimmed from the start, but not from the end. To trim
166/// end spaces we should lookahead by one deserializer event (i. e. skip all
167/// comments and processing instructions).
168///
169/// [`Text`]: crate::events::Event::Text
170/// [`CData`]: crate::events::Event::CData
171#[derive(Clone, Debug, PartialEq, Eq)]
172pub enum PayloadEvent<'a> {
173    /// Start tag (with attributes) `<tag attr="value">`.
174    Start(BytesStart<'a>),
175    /// End tag `</tag>`.
176    End(BytesEnd<'a>),
177    /// Escaped character data between tags.
178    Text(BytesText<'a>),
179    /// Unescaped character data stored in `<![CDATA[...]]>`.
180    CData(BytesCData<'a>),
181    /// Document type definition data (DTD) stored in `<!DOCTYPE ...>`.
182    DocType(BytesText<'a>),
183    /// Reference `&ref;` in the textual data.
184    GeneralRef(BytesRef<'a>),
185    /// End of XML document.
186    Eof,
187}
188
189impl<'a> PayloadEvent<'a> {
190    /// Ensures that all data is owned to extend the object's lifetime if necessary.
191    #[inline]
192    pub(crate) fn into_owned(self) -> PayloadEvent<'static> {
193        match self {
194            PayloadEvent::Start(e) => PayloadEvent::Start(e.into_owned()),
195            PayloadEvent::End(e) => PayloadEvent::End(e.into_owned()),
196            PayloadEvent::Text(e) => PayloadEvent::Text(e.into_owned()),
197            PayloadEvent::CData(e) => PayloadEvent::CData(e.into_owned()),
198            PayloadEvent::DocType(e) => PayloadEvent::DocType(e.into_owned()),
199            PayloadEvent::GeneralRef(e) => PayloadEvent::GeneralRef(e.into_owned()),
200            PayloadEvent::Eof => PayloadEvent::Eof,
201        }
202    }
203}
204
205/// An intermediate reader that consumes [`PayloadEvent`]s and produces final [`DeEvent`]s.
206/// [`PayloadEvent::Text`] events, that followed by any event except
207/// [`PayloadEvent::Text`] or [`PayloadEvent::CData`], are trimmed from the end.
208pub(crate) struct XmlReader<'i, R: XmlRead<'i>, E: EntityResolver = PredefinedEntityResolver> {
209    /// A source of low-level XML events
210    pub(crate) reader: R,
211    /// Intermediate event, that could be returned by the next call to `next()`.
212    /// If that is the `Text` event then leading spaces already trimmed, but
213    /// trailing spaces is not. Before the event will be returned, trimming of
214    /// the spaces could be necessary
215    lookahead: Result<PayloadEvent<'i>, DeError>,
216
217    /// Used to resolve unknown entities that would otherwise cause the parser
218    /// to return an [`EscapeError::UnrecognizedEntity`] error.
219    ///
220    /// [`EscapeError::UnrecognizedEntity`]: crate::escape::EscapeError::UnrecognizedEntity
221    entity_resolver: E,
222}
223
224impl<'i, R: XmlRead<'i>, E: EntityResolver> XmlReader<'i, R, E> {
225    pub(crate) fn new(mut reader: R, entity_resolver: E) -> Self {
226        // Lookahead by one event immediately, so we do not need to check in the
227        // loop if we need lookahead or not
228        let lookahead = reader.next();
229
230        Self {
231            reader,
232            lookahead,
233            entity_resolver,
234        }
235    }
236
237    /// Returns `true` if all events was consumed
238    pub(crate) const fn is_empty(&self) -> bool {
239        matches!(self.lookahead, Ok(PayloadEvent::Eof))
240    }
241
242    /// Read next event and put it in lookahead, return the current lookahead
243    #[inline(always)]
244    fn next_impl(&mut self) -> Result<PayloadEvent<'i>, DeError> {
245        replace(&mut self.lookahead, self.reader.next())
246    }
247
248    /// Returns `true` when next event is not a text event in any form.
249    #[inline(always)]
250    const fn current_event_is_last_text(&self) -> bool {
251        // If next event is a text-like event or a DocType (which is
252        // metadata and invisible to the data model), we should not
253        // trim trailing spaces — there is more content to drain, and
254        // any DocType between us and the next text run needs to be
255        // absorbed so `read_text` does not later see two consecutive
256        // `DeEvent::Text`. Without DocType here, an input like
257        // `<a>x<!DOCTYPE y>z</a>` produces two text events for `x`
258        // and `z`, tripping `unreachable!()` in `read_text`.
259        !matches!(
260            self.lookahead,
261            Ok(PayloadEvent::Text(_)
262                | PayloadEvent::CData(_)
263                | PayloadEvent::GeneralRef(_)
264                | PayloadEvent::DocType(_))
265        )
266    }
267
268    /// Read all consequent [`Text`] and [`CData`] events until non-text event
269    /// occurs. Content of all events would be appended to `result` and returned
270    /// as [`DeEvent::Text`].
271    ///
272    /// DocType events that fall between text events are absorbed by the
273    /// entity resolver and do not break the run — see
274    /// [`Self::current_event_is_last_text`] for the rationale.
275    ///
276    /// [`Text`]: PayloadEvent::Text
277    /// [`CData`]: PayloadEvent::CData
278    fn drain_text(&mut self, mut result: Cow<'i, str>) -> Result<DeEvent<'i>, DeError> {
279        loop {
280            if self.current_event_is_last_text() {
281                break;
282            }
283
284            match self.next_impl()? {
285                PayloadEvent::Text(e) => result
286                    .to_mut()
287                    .push_str(&e.xml_content(self.reader.xml_version())?),
288                PayloadEvent::CData(e) => result
289                    .to_mut()
290                    .push_str(&e.xml_content(self.reader.xml_version())?),
291                PayloadEvent::GeneralRef(e) => self.resolve_reference(result.to_mut(), e)?,
292                PayloadEvent::DocType(e) => {
293                    self.entity_resolver
294                        .capture(e)
295                        .map_err(|err| DeError::Custom(format!("cannot parse DTD: {}", err)))?;
296                }
297
298                // SAFETY: current_event_is_last_text checks that event is Text, CData, GeneralRef, or DocType
299                _ => unreachable!(
300                    "Only `Text`, `CData`, `GeneralRef` or `DocType` events can come here"
301                ),
302            }
303        }
304        Ok(DeEvent::Text(Text::new(result)))
305    }
306
307    /// Return an input-borrowing event.
308    pub(crate) fn next(&mut self) -> Result<DeEvent<'i>, DeError> {
309        loop {
310            return match self.next_impl()? {
311                PayloadEvent::Start(e) => Ok(DeEvent::Start(e)),
312                PayloadEvent::End(e) => Ok(DeEvent::End(e)),
313                PayloadEvent::Text(e) => self.drain_text(e.xml_content(self.reader.xml_version())?),
314                PayloadEvent::CData(e) => {
315                    self.drain_text(e.xml_content(self.reader.xml_version())?)
316                }
317                PayloadEvent::DocType(e) => {
318                    self.entity_resolver
319                        .capture(e)
320                        .map_err(|err| DeError::Custom(format!("cannot parse DTD: {}", err)))?;
321                    continue;
322                }
323                PayloadEvent::GeneralRef(e) => {
324                    let mut text = String::new();
325                    self.resolve_reference(&mut text, e)?;
326                    self.drain_text(text.into())
327                }
328                PayloadEvent::Eof => Ok(DeEvent::Eof),
329            };
330        }
331    }
332
333    fn resolve_reference(&mut self, result: &mut String, event: BytesRef) -> Result<(), DeError> {
334        let len = event.len();
335        let reference = self.decoder().decode(&event)?;
336
337        if let Some(num) = reference.strip_prefix('#') {
338            let codepoint = parse_number(num).map_err(EscapeError::InvalidCharRef)?;
339            result.push_str(codepoint.encode_utf8(&mut [0u8; 4]));
340            return Ok(());
341        }
342        if let Some(value) = self.entity_resolver.resolve(reference.as_ref()) {
343            result.push_str(value);
344            return Ok(());
345        }
346        Err(EscapeError::UnrecognizedEntity(0..len, reference.to_string()).into())
347    }
348
349    #[inline]
350    pub(crate) fn read_to_end(&mut self, name: QName) -> Result<(), DeError> {
351        match self.lookahead {
352            // We pre-read event with the same name that is required to be skipped.
353            // First call of `read_to_end` will end out pre-read event, the second
354            // will consume other events
355            Ok(PayloadEvent::Start(ref e)) if e.name() == name => {
356                let result1 = self.reader.read_to_end(name);
357                let result2 = self.reader.read_to_end(name);
358
359                // In case of error `next_impl` returns `Eof`
360                let _ = self.next_impl();
361                result1?;
362                result2?;
363            }
364            // We pre-read event with the same name that is required to be skipped.
365            // Because this is end event, we already consume the whole tree, so
366            // nothing to do, just update lookahead
367            Ok(PayloadEvent::End(ref e)) if e.name() == name => {
368                let _ = self.next_impl();
369            }
370            Ok(_) => {
371                let result = self.reader.read_to_end(name);
372
373                // In case of error `next_impl` returns `Eof`
374                let _ = self.next_impl();
375                result?;
376            }
377            // Read next lookahead event, unpack error from the current lookahead
378            Err(_) => {
379                self.next_impl()?;
380            }
381        }
382        Ok(())
383    }
384
385    #[inline]
386    pub(crate) fn decoder(&self) -> Decoder {
387        self.reader.decoder()
388    }
389}