Skip to main content

xml/
reader.rs

1//! Contains high-level interface for a pull-based XML parser.
2//!
3//! The most important type in this module is `EventReader`, which provides an iterator
4//! view for events in XML document.
5
6use std::io::Read;
7use std::iter::FusedIterator;
8use std::result;
9
10use crate::common::{Position, TextPosition};
11
12pub use config::ParserConfig;
13pub use error::{Error, ErrorKind, ImmutableEntitiesError};
14pub use events::{DoctypeRef, XmlEvent};
15
16// back compat
17#[doc(hidden)]
18#[deprecated(note = "Merged into ParserConfig")]
19pub type ParserConfig2 = ParserConfig;
20
21use self::parser::PullParser;
22
23mod config;
24mod error;
25mod events;
26mod lexer;
27mod parser;
28
29/// A result type yielded by `XmlReader`.
30pub type Result<T, E = Error> = result::Result<T, E>;
31
32/// A wrapper around an `std::io::Read` instance which provides pull-based XML parsing.
33///
34/// The reader should be wrapped in a `BufReader`, otherwise parsing may be very slow.
35pub struct EventReader<R: Read> {
36    source: R,
37    parser: PullParser,
38}
39
40impl<R: Read> EventReader<R> {
41    /// Creates a new reader, consuming the given stream. The reader should be wrapped in a `BufReader`, otherwise parsing may be very slow.
42    #[inline]
43    pub fn new(source: R) -> Self {
44        Self::new_with_config(source, ParserConfig::new())
45    }
46
47    /// Creates a new reader with the provded configuration, consuming the given stream. The reader should be wrapped in a `BufReader`, otherwise parsing may be very slow.
48    #[inline]
49    pub fn new_with_config(source: R, config: impl Into<ParserConfig>) -> Self {
50        Self {
51            source,
52            parser: PullParser::new(config),
53        }
54    }
55
56    /// Pulls and returns next XML event from the stream.
57    ///
58    /// If this returns [Err] or [`XmlEvent::EndDocument`] then further calls to
59    /// this method will return this event again.
60    #[inline]
61    #[allow(clippy::should_implement_trait)]
62    pub fn next(&mut self) -> Result<XmlEvent> {
63        self.parser.next(&mut self.source)
64    }
65
66    /// Skips all XML events until the next end tag at the current level.
67    ///
68    /// Convenience function that is useful for the case where you have
69    /// encountered a start tag that is of no interest and want to
70    /// skip the entire XML subtree until the corresponding end tag.
71    #[inline]
72    pub fn skip(&mut self) -> Result<()> {
73        let mut depth = 1;
74
75        while depth > 0 {
76            match self.next()? {
77                XmlEvent::StartElement { .. } => depth += 1,
78                XmlEvent::EndElement { .. } => depth -= 1,
79                XmlEvent::EndDocument => return Err(Error {
80                    kind: ErrorKind::UnexpectedEof,
81                    pos: self.parser.position(),
82                }),
83                _ => {},
84            }
85        }
86
87        Ok(())
88    }
89
90    /// Access underlying reader
91    ///
92    /// Using it directly while the event reader is parsing is not recommended
93    pub fn source(&self) -> &R { &self.source }
94
95    /// Access underlying reader
96    ///
97    /// Using it directly while the event reader is parsing is not recommended
98    pub fn source_mut(&mut self) -> &mut R { &mut self.source }
99
100    /// Unwraps this `EventReader`, returning the underlying reader.
101    ///
102    /// Note that this operation is destructive; unwrapping the reader and wrapping it
103    /// again with `EventReader::new()` will create a fresh reader which will attempt
104    /// to parse an XML document from the beginning.
105    pub fn into_inner(self) -> R {
106        self.source
107    }
108
109    /// Returns the DOCTYPE of the document if it has already been seen
110    ///
111    /// Available only after the `Doctype` event
112    #[inline]
113    #[deprecated(note = "there is `XmlEvent::Doctype` now")]
114    #[allow(deprecated)]
115    pub fn doctype(&self) -> Option<&str> {
116        self.parser.doctype()
117    }
118
119    /// Returns PUBLIC/SYSTEM DOCTYPE IDs if it has already been seen
120    ///
121    /// Available only after the `Doctype` event
122    #[inline]
123    pub fn doctype_ids(&self) -> Option<DoctypeRef<'_>> {
124        self.parser.doctype_ids()
125    }
126
127    /// Add new entity definitions **before any XML elements have been parsed**.
128    ///
129    /// ## Errors
130    ///
131    /// It's valid to call this after DOCTYPE, but not later. It won't be possible to add entities to a document without either XML decl or DOCTYPE.
132    ///
133    /// It will fail if the document is declared as _standalone_.
134    #[inline]
135    pub fn add_entities<S: Into<String>, T: Into<String>>(&mut self, entities: impl IntoIterator<Item=(S, T)>) -> result::Result<(), ImmutableEntitiesError> {
136        self.parser.add_entities(entities)
137    }
138}
139
140impl<B: Read> Position for EventReader<B> {
141    /// Returns the position of the last event produced by the reader.
142    #[inline]
143    fn position(&self) -> TextPosition {
144        self.parser.position()
145    }
146}
147
148impl<R: Read> IntoIterator for EventReader<R> {
149    type IntoIter = Events<R>;
150    type Item = Result<XmlEvent>;
151
152    fn into_iter(self) -> Events<R> {
153        Events { reader: self, finished: false }
154    }
155}
156
157impl<R: Read + Clone> Clone for EventReader<R> {
158    fn clone(&self) -> Self {
159        Self {
160            source: self.source.clone(),
161            parser: self.parser.clone()
162        }
163    }
164}
165
166/// An iterator over XML events created from some type implementing `Read`.
167///
168/// When the next event is `xml::event::Error` or `xml::event::EndDocument`, then
169/// it will be returned by the iterator once, and then it will stop producing events.
170pub struct Events<R: Read> {
171    reader: EventReader<R>,
172    finished: bool,
173}
174
175impl<R: Read> Events<R> {
176    /// Unwraps the iterator, returning the internal `EventReader`.
177    #[inline]
178    pub fn into_inner(self) -> EventReader<R> {
179        self.reader
180    }
181
182    /// Access the underlying reader
183    ///
184    /// It's not recommended to use it while the events are still being parsed
185    pub fn source(&self) -> &R { &self.reader.source }
186
187    /// Access the underlying reader
188    ///
189    /// It's not recommended to use it while the events are still being parsed
190    pub fn source_mut(&mut self) -> &mut R { &mut self.reader.source }
191}
192
193impl<R: Read> std::ops::Deref for Events<R> {
194    type Target = EventReader<R>;
195
196    fn deref(&self) -> &Self::Target {
197        &self.reader
198    }
199}
200
201impl<R: Read> std::ops::DerefMut for Events<R> {
202    fn deref_mut(&mut self) -> &mut Self::Target {
203        &mut self.reader
204    }
205}
206
207impl<R: Read> FusedIterator for Events<R> {
208}
209
210impl<R: Read> Iterator for Events<R> {
211    type Item = Result<XmlEvent>;
212
213    #[inline]
214    fn next(&mut self) -> Option<Result<XmlEvent>> {
215        if self.finished && !self.reader.parser.is_ignoring_end_of_stream() {
216            None
217        } else {
218            let ev = self.reader.next();
219            if let Ok(XmlEvent::EndDocument) | Err(_) = ev {
220                self.finished = true;
221            }
222            Some(ev)
223        }
224    }
225}
226
227impl<'r> EventReader<&'r [u8]> {
228    /// A convenience method to create an `XmlReader` from a string slice.
229    #[inline]
230    #[must_use]
231    #[allow(clippy::should_implement_trait)]
232    pub fn from_str(source: &'r str) -> Self {
233        EventReader::new(source.as_bytes())
234    }
235}