Skip to main content

eml_nl/io/
reader.rs

1use std::{borrow::Cow, collections::HashMap};
2
3use quick_xml::{
4    NsReader, XmlVersion,
5    escape::unescape,
6    events::{BytesStart, Event},
7    name::{QName, ResolveResult},
8};
9
10use crate::{
11    MultipleEMLErrors,
12    error::{EMLError, EMLErrorKind, EMLResultExt},
13    io::QualifiedName,
14    utils::{StringValue, StringValueData},
15};
16
17/// Reading EML documents from a string slice.
18pub trait EMLRead {
19    /// Parse an EML document from the given string slice.
20    ///
21    /// The `parsing_mode` parameter indicates whether strict parsing of values
22    /// (e.g. dates, numbers) should be performed. If set to Strict, any parsing
23    /// error will fail immediately. If set to StrictFallback, parsing errors
24    /// will be collected and the raw string value will be used instead. If set
25    /// to Loose, no parsing will be performed and all values will be stored as
26    /// raw strings.
27    fn parse_eml(input: &str, parsing_mode: EMLParsingMode) -> EMLReadResult<Self>
28    where
29        Self: Sized;
30}
31
32/// The result of reading an EML document, which may include non-fatal errors.
33#[must_use]
34pub enum EMLReadResult<T> {
35    /// The document was parsed successfully, with optional non-fatal errors.
36    Ok(T, Vec<EMLError>),
37    /// The document could not be parsed due to fatal errors.
38    Err(EMLError),
39}
40
41impl<T> EMLReadResult<T> {
42    /// Returns the list of errors (fatal and non-fatal)
43    pub fn errors(&self) -> &[EMLError] {
44        match self {
45            EMLReadResult::Ok(_, errors) => errors,
46            EMLReadResult::Err(EMLError::Multiple(MultipleEMLErrors { errors })) => errors,
47            EMLReadResult::Err(err) => std::slice::from_ref(err),
48        }
49    }
50
51    /// Converts this result into a standard Result, returning the value if
52    /// successful, or the error(s) if not.
53    pub fn ok(self) -> Result<T, EMLError> {
54        self.into()
55    }
56
57    /// Converts this result into a standard Result, returning the value and
58    /// the list of non-fatal errors if successful, or the error(s) if not.
59    pub fn ok_with_errors(self) -> Result<(T, Vec<EMLError>), EMLError> {
60        self.into()
61    }
62
63    /// Unwraps the value if successful, or panics if not.
64    pub fn unwrap(self) -> T {
65        self.ok().unwrap()
66    }
67
68    /// Unwraps the value if successful, or panics with the given message if not.
69    pub fn expect(self, msg: &str) -> T {
70        self.ok().expect(msg)
71    }
72}
73
74impl<T> From<EMLReadResult<T>> for Result<T, EMLError> {
75    fn from(value: EMLReadResult<T>) -> Self {
76        match value {
77            EMLReadResult::Ok(doc, _) => Ok(doc),
78            EMLReadResult::Err(e) => Err(e),
79        }
80    }
81}
82
83impl<T> From<EMLReadResult<T>> for Result<(T, Vec<EMLError>), EMLError> {
84    fn from(value: EMLReadResult<T>) -> Self {
85        match value {
86            EMLReadResult::Ok(doc, errors) => Ok((doc, errors)),
87            EMLReadResult::Err(e) => Err(e),
88        }
89    }
90}
91
92impl<T> EMLRead for T
93where
94    T: EMLReadElement + 'static,
95{
96    fn parse_eml(input: &str, parsing_mode: EMLParsingMode) -> EMLReadResult<Self>
97    where
98        Self: Sized + 'static,
99    {
100        let mut reader = EMLReader::init_from_str(input, parsing_mode);
101        let res = reader.with_next_element(|r| T::read_eml_element(r));
102
103        let e = match res {
104            Ok(doc) => return EMLReadResult::Ok(doc, reader.errors),
105            Err(e) => e,
106        };
107
108        if reader.errors.is_empty() {
109            EMLReadResult::Err(e)
110        } else {
111            EMLReadResult::Err(EMLError::from_vec_with_additional(reader.errors, e))
112        }
113    }
114}
115
116/// This trait should be implemented by all types that can be parsed from EML files.
117pub(crate) trait EMLReadElement {
118    fn read_eml_element<'a, 'b>(elem: &mut EMLElementReader<'a, 'b>) -> Result<Self, EMLError>
119    where
120        Self: Sized + 'static;
121}
122
123/// A span in the input data, represented as byte offsets.
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub struct Span {
126    /// Start byte offset of the span (inclusive).
127    pub start: u64,
128    /// End byte offset of the span (exclusive).
129    pub end: u64,
130}
131
132impl Span {
133    /// Create a new span from the given start and end byte offsets.
134    pub fn new(start: u64, end: u64) -> Span {
135        Span { start, end }
136    }
137}
138
139impl std::fmt::Display for Span {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        write!(f, "{} until {}", self.start, self.end)
142    }
143}
144
145/// The mode to use when parsing values in EML files.
146///
147/// This enum defines how strict the library handles parsing of several values
148/// and known issues in EML files. In strict mode any issue will immediately
149/// cause a parsing error and parsing will fail right away. With fallback,
150/// whenever we encounter an issue that is recoverable we continue parsing.
151/// With loose mode many parsing operations aren't even attempted and many
152/// values are just stored as raw strings. Also take a look at the documentation
153/// for [`StringValue`] for more information on how these string/parsed values
154/// are handled in the different modes and how to use them.
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum EMLParsingMode {
157    /// Require strict parsing of all stringly values to their respective types
158    Strict,
159
160    /// Try to parse stringly values, but fall back to raw strings on failure.
161    ///
162    /// This mode will collect errors to allow reporting them later.
163    StrictFallback,
164
165    /// Do not attempt to parse stringly values, always store raw strings.
166    Loose,
167}
168
169impl EMLParsingMode {
170    /// Returns whether the parsing mode is `Strict`.
171    pub fn is_strict(&self) -> bool {
172        matches!(self, EMLParsingMode::Strict)
173    }
174}
175
176/// The main EML XML reader.
177///
178/// We require all EML files to be fully loaded in memory, so this reader only
179/// works on byte slices. Furthermore, all files should be encoded in UTF-8.
180pub(crate) struct EMLReader<'a> {
181    inner: NsReader<&'a [u8]>,
182    xml_version: XmlVersion,
183    parsing_mode: EMLParsingMode,
184    errors: Vec<EMLError>,
185}
186
187impl<'a> EMLReader<'a> {
188    /// Create this reader from a string slice.
189    pub fn init_from_str(data: &'a str, parsing_mode: EMLParsingMode) -> EMLReader<'a> {
190        Self::from_reader(NsReader::from_str(data), parsing_mode)
191    }
192
193    pub fn from_reader(reader: NsReader<&'a [u8]>, parsing_mode: EMLParsingMode) -> EMLReader<'a> {
194        EMLReader {
195            inner: reader,
196            xml_version: XmlVersion::default(),
197            parsing_mode,
198            errors: Vec::new(),
199        }
200    }
201
202    fn next(&mut self) -> Result<(Event<'a>, Span), EMLError> {
203        let span_start = self.inner.buffer_position();
204        let event = self.inner.read_event();
205        let event = match event {
206            Ok(evt) => evt,
207            Err(xml_err) => {
208                let error_pos = self.inner.error_position();
209                if error_pos == 0 {
210                    // quick-xml returns error position 0 when it doesn't have an error yet,
211                    // but if we do end up here we know the error must have happened somewhere
212                    // after the end of the previous event and where-ever the current buffer
213                    // position is.
214                    return Err(xml_err)
215                        .with_span(Span::new(span_start, self.inner.buffer_position()));
216                } else {
217                    return Err(xml_err).with_span(Span::new(error_pos, error_pos));
218                }
219            }
220        };
221        let span = Span::new(span_start, self.inner.buffer_position());
222
223        // Capture XML version from declaration
224        if let Event::Decl(d) = &event {
225            self.xml_version = d.xml_version().with_span(span)?;
226        }
227
228        Ok((event, span))
229    }
230
231    pub fn next_element<'tmp>(&'tmp mut self) -> Result<EMLElementReader<'tmp, 'a>, EMLError> {
232        loop {
233            match self.next()? {
234                (Event::Start(start), span) => {
235                    return Ok(EMLElementReader::from_start(self, start, false, span));
236                }
237                (Event::Empty(start), span) => {
238                    return Ok(EMLElementReader::from_start(self, start, true, span));
239                }
240                (Event::Eof, span) => {
241                    return Err(EMLErrorKind::UnexpectedEof).with_span(span);
242                }
243                _other => {
244                    // Ignore other events
245                }
246            }
247        }
248    }
249
250    pub fn with_next_element<R>(
251        &mut self,
252        f: impl for<'d, 'e> FnOnce(&mut EMLElementReader<'d, 'e>) -> Result<R, EMLError>,
253    ) -> Result<R, EMLError> {
254        let mut root = self.next_element()?;
255        f(&mut root)
256    }
257}
258
259/// A reader for an XML element in an EML file.
260///
261/// This reader tries to ensure that the entire element is consumed before it
262/// is dropped, but it is recommended to explicitly call `skip` to completely
263/// consume the element.
264pub(crate) struct EMLElementReader<'r, 'input> {
265    reader: &'r mut EMLReader<'input>,
266    start: BytesStart<'input>,
267    depth: usize,
268    found_matching_end: bool,
269    is_empty: bool,
270    span: Span,
271    last_span: Span,
272}
273
274impl<'r, 'input> EMLElementReader<'r, 'input> {
275    /// Given a start event that was just read from the reader, create a element
276    /// reader until the matching end tag. If the start event was an empty
277    /// element, this must be indicated using the `is_empty` parameter, otherwise
278    /// the reader will parse this document invalidly.
279    ///
280    /// The span should be the span of the start event.
281    pub fn from_start(
282        reader: &'r mut EMLReader<'input>,
283        start: BytesStart<'input>,
284        is_empty: bool,
285        span: Span,
286    ) -> EMLElementReader<'r, 'input> {
287        EMLElementReader {
288            reader,
289            start,
290            depth: 1,
291            found_matching_end: is_empty,
292            is_empty,
293            span,
294            last_span: span,
295        }
296    }
297
298    /// Extracts the resolved name of this element as a tuple of local name and
299    /// an optional namespace URI.
300    pub fn name(&self) -> Result<QualifiedName<'_, '_>, EMLError> {
301        self.get_resolved_name(self.start.name(), self.span, false)
302    }
303
304    /// Checks if this element has the given local name and optional namespace URI.
305    pub fn has_name<'a, 'b>(
306        &self,
307        name: impl Into<QualifiedName<'a, 'b>>,
308    ) -> Result<bool, EMLError> {
309        self.is_resolved_name(self.start.name(), self.span, name, false)
310    }
311
312    /// Find the next child element of this element and return a reader for that
313    /// part of the document. The returned reader must be fully consumed before
314    /// continuing to read from this element. If the entire element is consumed,
315    /// this will return None.
316    pub fn next_child(&mut self) -> Result<Option<EMLElementReader<'_, 'input>>, EMLError> {
317        loop {
318            match self.next()? {
319                Some((Event::Start(start), span)) => {
320                    self.depth -= 1; // the child must handle the end tag itself
321                    return Ok(Some(EMLElementReader::from_start(
322                        self.reader,
323                        start,
324                        false,
325                        span,
326                    )));
327                }
328                Some((Event::Empty(start), span)) => {
329                    return Ok(Some(EMLElementReader::from_start(
330                        self.reader,
331                        start,
332                        true,
333                        span,
334                    )));
335                }
336                None => return Ok(None),
337                _other => {
338                    // Ignore other events
339                }
340            }
341        }
342    }
343
344    /// Get the value of an attribute. If the attribute does not exist this will
345    /// return an error.
346    pub fn attribute_value_req<'a, 'b>(
347        &self,
348        name: impl Into<QualifiedName<'a, 'b>>,
349    ) -> Result<Cow<'_, str>, EMLError> {
350        let name = name.into();
351        self.attribute_value(name.clone())?
352            .ok_or_else(|| EMLErrorKind::MissingAttribute(name.as_owned()))
353            .with_span(self.span)
354    }
355
356    /// Get the value of an attribute. If the attribute does not exist this will
357    /// return None.
358    pub fn attribute_value<'a, 'b>(
359        &self,
360        name: impl Into<QualifiedName<'a, 'b>>,
361    ) -> Result<Option<Cow<'_, str>>, EMLError> {
362        let name = name.into();
363        // quick-xml does not expose any way to get the span of individual attributes, so we use the whole start tag span for now
364        for attr in self.start.attributes() {
365            let attr = attr.with_span(self.span)?;
366            if self.is_resolved_name(attr.key, self.span, name.clone(), true)? {
367                return Ok(Some(
368                    attr.decoded_and_normalized_value(
369                        self.reader.xml_version,
370                        self.reader.inner.decoder(),
371                    )
372                    .with_span(self.span)?,
373                ));
374            }
375        }
376        Ok(None)
377    }
378
379    /// Get a hashmap of all attributes of the start tag of this element.
380    #[expect(unused)]
381    pub fn attributes(&self) -> Result<HashMap<QualifiedName<'_, '_>, Cow<'_, str>>, EMLError> {
382        let mut attributes = HashMap::new();
383        // quick-xml does not expose any way to get the span of individual attributes, so we use the whole start tag span for now
384        for attr in self.start.attributes() {
385            let attr = attr.with_span(self.span)?;
386            let name = self.get_resolved_name(attr.key, self.span, true)?;
387            let value = attr
388                .decoded_and_normalized_value(self.reader.xml_version, self.reader.inner.decoder())
389                .with_span(self.span)?;
390            attributes.insert(name, value);
391        }
392        Ok(attributes)
393    }
394
395    /// Extracts the text content of this element. If the element is an empty
396    /// element or the element contains no text, this returns None.
397    pub fn text_without_children_opt(&mut self) -> Result<Option<Box<str>>, EMLError> {
398        if self.is_empty {
399            Ok(None)
400        } else {
401            let text = self.text_without_children()?;
402            if text.is_empty() {
403                Ok(None)
404            } else {
405                Ok(Some(text))
406            }
407        }
408    }
409
410    /// Extracts the text content of this element, consuming all events until
411    /// the end of the element. If anything other than text is found, this will
412    /// return an error (not consuming everything).
413    pub fn text_without_children(&mut self) -> Result<Box<str>, EMLError> {
414        let mut text = String::new();
415        loop {
416            match self.next()? {
417                Some((Event::Text(t), span)) => {
418                    let decoded = t.xml_content(self.reader.xml_version).with_span(span)?;
419                    text.push_str(decoded.as_ref());
420                }
421                Some((Event::CData(t), span)) => {
422                    let decoded = t.xml_content(self.reader.xml_version).with_span(span)?;
423                    text.push_str(decoded.as_ref());
424                }
425                Some((Event::GeneralRef(r), span)) => {
426                    let ref_name = r.decode().with_span(span)?;
427                    let formatted_entity = format!("&{};", ref_name);
428
429                    text.push_str(unescape(&formatted_entity).with_span(span)?.as_ref());
430                }
431                Some((Event::Comment(_), _)) => {
432                    // Ignore comments
433                }
434                None => break,
435                Some((_other, span)) => {
436                    return Err(EMLErrorKind::UnexpectedEvent).with_span(span);
437                }
438            }
439        }
440        Ok(text.into())
441    }
442
443    /// Skip all remaining content/events in this element. Stops reading just
444    /// after the matching end tag.
445    pub fn skip(&mut self) -> Result<(), EMLError> {
446        while let Some(_evt) = self.next()? {
447            // Just consume events until the end of this element
448        }
449        Ok(())
450    }
451
452    /// Returns the span of the start tag of this element.
453    pub fn span(&self) -> Span {
454        self.span
455    }
456
457    /// Returns the span of the last event that was read from this element.
458    pub fn last_span(&self) -> Span {
459        self.last_span
460    }
461
462    /// Returns the full span of this element up until the current event,
463    /// including the start tag. If the entire element has been consumed, this
464    /// will return the full span of the element.
465    pub fn full_span(&self) -> Span {
466        Span::new(self.span.start, self.last_span.end)
467    }
468
469    /// Returns the inner span of this element, excluding the start tag. The
470    /// returned span does not include the span of the last read event. If the
471    /// entire element has been consumed, this will return the span between the
472    /// start and end tags.
473    pub fn inner_span(&self) -> Span {
474        Span::new(self.span.end, self.last_span.start)
475    }
476
477    /// Returns whether strict value parsing is enabled for this reader
478    pub fn parsing_mode(&self) -> EMLParsingMode {
479        self.reader.parsing_mode
480    }
481
482    /// Pushes an error to the reader's error collection.
483    pub fn push_err(&mut self, err: EMLError) {
484        if let EMLError::Multiple(MultipleEMLErrors { errors }) = err {
485            self.reader.errors.extend(errors);
486        } else {
487            self.reader.errors.push(err);
488        }
489    }
490
491    /// Maps a parsing error to an EMLError with context about this element.
492    fn map_value_error<'a, 'b, T: StringValueData>(
493        &self,
494        parsing_error: Result<StringValue<T>, T::Error>,
495        name: QualifiedName<'a, 'b>,
496        span: Span,
497    ) -> Result<StringValue<T>, EMLError> {
498        parsing_error.map_err(|e| EMLError::invalid_value(name.as_owned(), e, Some(span)))
499    }
500
501    /// Reads a StringValue of the given type from the provided text.
502    ///
503    /// The exact parsing behavior depends on the parsing mode set in the reader.
504    pub(crate) fn string_value_from_text<'a, 'b, T: StringValueData>(
505        &mut self,
506        text: Box<str>,
507        name: Option<QualifiedName<'a, 'b>>,
508        span: Span,
509    ) -> Result<StringValue<T>, EMLError> {
510        let name = name.map_or_else(|| self.name(), Ok)?;
511        match self.parsing_mode() {
512            EMLParsingMode::Strict => {
513                self.map_value_error(StringValue::<T>::from_raw_parsed(&text), name, span)
514            }
515            EMLParsingMode::StrictFallback => {
516                match self.map_value_error(StringValue::<T>::from_raw_parsed(&text), name, span) {
517                    Ok(v) => Ok(v),
518                    Err(e) => {
519                        self.push_err(e);
520                        Ok(StringValue::<T>::Raw(text))
521                    }
522                }
523            }
524            EMLParsingMode::Loose => Ok(StringValue::Raw(text)),
525        }
526    }
527
528    /// Reads the value of this element as a StringValue of the given type.
529    ///
530    /// The exact parsing behavior depends on the parsing mode set in the reader;
531    /// only in [`EMLParsingMode::Strict`] mode will value parsing errors result in
532    /// an error being returned. In [`EMLParsingMode::StrictFallback`] mode,
533    /// value parsing errors will be stored, but the raw string value will be
534    /// returned instead. In [`EMLParsingMode::Loose`] mode, no parsing will be
535    /// performed and the raw string value will be returned.
536    pub fn string_value<T: StringValueData>(&mut self) -> Result<StringValue<T>, EMLError> {
537        let text = self.text_without_children()?;
538        self.string_value_from_text(text, None, self.inner_span())
539    }
540
541    /// Reads the value of this element as a StringValue of the given type,
542    /// returning None if the element is empty or contains no text.
543    ///
544    /// The exact parsing behavior depends on the parsing mode set in the reader;
545    /// only in [`EMLParsingMode::Strict`] mode will value parsing errors result in
546    /// an error being returned. In [`EMLParsingMode::StrictFallback`] mode,
547    /// value parsing errors will be stored, but the raw string value will be
548    /// returned instead. In [`EMLParsingMode::Loose`] mode, no parsing will be
549    /// performed and the raw string value will be returned.
550    pub fn string_value_opt<T: StringValueData>(
551        &mut self,
552    ) -> Result<Option<StringValue<T>>, EMLError> {
553        self.text_without_children_opt()?
554            .map(|v| self.string_value_from_text(v, None, self.inner_span()))
555            .transpose()
556    }
557
558    /// Reads the value of the given attribute as a StringValue of the given type.
559    ///
560    /// If the attribute does not exist, None is returned. The exact parsing
561    /// behavior depends on the parsing mode set in the reader, as described in
562    /// [`EMLElementReader::string_value`].
563    pub fn string_value_attr_opt<'a, 'b, T: StringValueData>(
564        &mut self,
565        attr_name: impl Into<QualifiedName<'a, 'b>>,
566    ) -> Result<Option<StringValue<T>>, EMLError> {
567        let attr_name = attr_name.into();
568        match self.attribute_value(attr_name.clone())? {
569            Some(value) => Ok(Some(self.string_value_from_text(
570                value.into(),
571                Some(attr_name),
572                self.span(),
573            )?)),
574            None => Ok(None),
575        }
576    }
577
578    /// Reads the value of the given attribute as a StringValue of the given type.
579    ///
580    /// If the attribute does not exist, an error is returned, unless a default
581    /// value is provided. The exact parsing behavior depends on the parsing
582    /// mode set in the reader, as described in [`EMLElementReader::string_value`].
583    pub fn string_value_attr<'a, 'b, T: StringValueData>(
584        &mut self,
585        attr_name: impl Into<QualifiedName<'a, 'b>>,
586        default_value: Option<&str>,
587    ) -> Result<StringValue<T>, EMLError> {
588        let attr_name = attr_name.into();
589        let value = self
590            .attribute_value(attr_name.clone())?
591            .or_else(|| default_value.map(Cow::Borrowed));
592        match value {
593            Some(value) => self.string_value_from_text(value.into(), Some(attr_name), self.span()),
594            None => {
595                Err(EMLErrorKind::MissingAttribute(attr_name.as_owned())).with_span(self.span())
596            }
597        }
598    }
599
600    /// Extracts the namespace URI from a ResolveResult.
601    fn namespace_name<'a>(
602        &self,
603        resolve_result: ResolveResult<'a>,
604        span: Span,
605    ) -> Result<Option<Cow<'a, str>>, EMLError> {
606        match resolve_result {
607            ResolveResult::Bound(namespace) => Ok(Some(
608                self.reader
609                    .inner
610                    .decoder()
611                    .decode(namespace.into_inner())
612                    .with_span(span)?,
613            )),
614            ResolveResult::Unbound => Ok(None),
615            ResolveResult::Unknown(scope) => Err(EMLErrorKind::UnknownNamespace(
616                self.reader
617                    .inner
618                    .decoder()
619                    .decode(&scope)
620                    .with_span(span)?
621                    .into_owned(),
622            ))
623            .with_span(span),
624        }
625    }
626
627    /// Checks if the given qualified name is of the expected local name and
628    /// optional namespace URI.
629    fn is_resolved_name<'a, 'b, 'c>(
630        &self,
631        name: QName<'a>,
632        span: Span,
633        expected_name: impl Into<QualifiedName<'b, 'c>>,
634        is_attribute: bool,
635    ) -> Result<bool, EMLError> {
636        let expected_name = expected_name.into();
637        let resolved_name = self.get_resolved_name(name, span, is_attribute)?;
638        let matches_local = resolved_name.local_name.as_ref() == expected_name.local_name.as_ref();
639        let matches_namespace = match (
640            expected_name.namespace.as_deref(),
641            resolved_name.namespace.as_deref(),
642        ) {
643            (Some(expected), Some(found)) => expected == found,
644            (None, None) => true,
645            _ => false,
646        };
647        Ok(matches_local && matches_namespace)
648    }
649
650    /// Extracts the resolved local name and optional namespace URI from the
651    /// given qualified name (i.e. name that may include a prefix such as
652    /// `xmlns:eml`)
653    fn get_resolved_name<'a>(
654        &'a self,
655        name: QName<'a>,
656        span: Span,
657        is_attribute: bool,
658    ) -> Result<QualifiedName<'a, 'a>, EMLError> {
659        let (resolved, local_name) = if is_attribute {
660            self.reader.inner.resolver().resolve_attribute(name)
661        } else {
662            self.reader.inner.resolver().resolve_element(name)
663        };
664        let namespace = self.namespace_name(resolved, span)?;
665        let local_name = self
666            .reader
667            .inner
668            .decoder()
669            .decode(local_name.into_inner())
670            .with_span(span)?;
671
672        Ok(QualifiedName::new(local_name, namespace))
673    }
674
675    /// Reads the next event from this element, returning None if the end of
676    /// this element has been reached.
677    fn next(&mut self) -> Result<Option<(Event<'input>, Span)>, EMLError> {
678        if self.found_matching_end {
679            return Ok(None);
680        }
681
682        let (evt, span) = self.reader.next()?;
683        self.last_span = span;
684        if matches!(evt, Event::Start(_)) {
685            self.depth += 1;
686        }
687
688        if matches!(evt, Event::End(_)) {
689            self.depth -= 1;
690        }
691
692        if matches!(evt, Event::Eof) {
693            return Err(EMLErrorKind::UnexpectedEof).with_span(span);
694        }
695
696        if self.depth == 0
697            && let Event::End(e) = &evt
698        {
699            if e.name() == self.start.name() {
700                self.found_matching_end = true;
701                return Ok(None);
702            } else {
703                return Err(EMLErrorKind::UnexpectedEndElement).with_span(span);
704            }
705        }
706
707        Ok(Some((evt, span)))
708    }
709
710    /// Returns whether this element is empty (i.e., has no content and no end tag).
711    pub fn is_empty(&self) -> bool {
712        self.is_empty
713    }
714}
715
716impl Drop for EMLElementReader<'_, '_> {
717    fn drop(&mut self) {
718        // Ensure we have consumed the entire element
719        let _ = self.skip();
720    }
721}
722
723macro_rules! collect_struct {
724    // This macro starts by first matching the external syntax and converting
725    // that to an internal representation that can be more easily processed.
726    // Once all tokens have been processed, we continue with the @emit rule.
727    // In this phase, we output the base structure of the code. In this phase,
728    // we again delegate to other rules to output specific parts of the code.
729    // These parts are: @decl: declares temporary variables that
730    // will hold the parsed values; @matcher: code to check for each field while
731    // reading children from the XML element; @process: code to process each
732    // value before storing in the struct; and @assign: code to assign the
733    // final values to the struct fields. This final part once again uses a
734    // recursive approach to output the assignments one by one because of
735    // limitations in macro_rules! that prevent us from directly outputting the
736    // list expansions as one (Rust stops expanding once it sees a macro in a
737    // field position and then fails with a syntax error).
738
739    // entry point of the macro, forward to expand rules
740    ( $root:expr, $ty:ident { $($rest:tt)* }) => {
741        collect_struct!(@expand [$root] [$ty] [] $($rest)* )
742    };
743
744    // accumulate for a normal row
745    ( @expand [$root:expr] [$ty:ident] [$($items:tt ; )*]
746        $field:ident: $namespaced_name:expr => |$var:ident| $map:expr ,
747        $($tail:tt)*
748    ) => {
749        collect_struct!(@expand [$root] [$ty] [
750            $($items ; )*
751            (@field [$field] [$namespaced_name] [$var] [$map]) ;
752        ] $($tail)*)
753    };
754
755    // accumulate for a parse-only row (not stored in struct)
756    ( @expand [$root:expr] [$ty:ident] [$($items:tt ; )*]
757        $field:ident as None: $namespaced_name:expr => |$var:ident| $map:expr ,
758        $($tail:tt)*
759    ) => {
760        collect_struct!(@expand [$root] [$ty] [
761            $($items ; )*
762            (@parse_only [$field] [$namespaced_name] [$var] [$map]) ;
763        ] $($tail)*)
764    };
765
766    // accumulate, for an option row
767    ( @expand [$root:expr] [$ty:ident] [$($items:tt ; )*]
768        $field:ident as Option: $namespaced_name:expr => |$var:ident| $map:expr ,
769        $($tail:tt)*
770    ) => {
771        collect_struct!(@expand [$root] [$ty] [
772            $($items ; )*
773            (@optional [$field] [$namespaced_name] [$var] [$map]) ;
774        ] $($tail)*)
775    };
776
777    // accumulate, for a btreemap row
778    ( @expand [$root:expr] [$ty:ident] [$($items:tt ; )*]
779        $field:ident as BTreeMap: $namespaced_name:expr => |$var:ident| $map:expr,
780        $($tail:tt)*
781    ) => {
782        collect_struct!(@expand [$root] [$ty] [
783            $($items ; )*
784            (@btreemap [$field] [$namespaced_name] [$var] [$map]) ;
785        ] $($tail)*)
786    };
787
788    // accumulate, for a vector row
789    ( @expand [$root:expr] [$ty:ident] [$($items:tt ; )*]
790        $field:ident as Vec: $namespaced_name:expr => |$var:ident| $map:expr ,
791        $($tail:tt)*
792    ) => {
793        collect_struct!(@expand [$root] [$ty] [
794            $($items ; )*
795            (@vec [$field] [$namespaced_name] [$var] [$map]) ;
796        ] $($tail)*)
797    };
798
799    // accumulate for a direct row
800    ( @expand [$root:expr] [$ty:ident] [$($items:tt ; )*]
801        $field:ident: $value:expr ,
802        $($tail:tt)*
803    ) => {
804        collect_struct!(@expand [$root] [$ty] [
805            $($items ; )*
806            (@direct [$field] [$value]) ;
807        ] $($tail)*)
808    };
809
810    // accumulation of items completed, start emitting
811    ( @expand [$root:expr] [$ty:ident] [$($items:tt ; )*] ) => {
812        collect_struct!(@emit [$root] [$ty] [$($items ; )*])
813    };
814
815    // Emit the actual code to read the struct
816    ( @emit [$root:expr] [$ty:ident] [$($items:tt ; )*] ) => {{
817        $( collect_struct!(@decl $items); )*
818
819        let elem_name = $root.name()?.as_owned();
820        while let Some(mut next_child) = $root.next_child()? {
821            let name = next_child.name()?.as_owned().into_inner();
822            #[allow(unused_mut)]
823            let mut handled = false;
824
825            $( collect_struct!(@matcher next_child, name, handled, $items); )*
826
827            if !handled {
828                next_child.push_err($crate::error::EMLError::Positioned {
829                    kind: $crate::error::EMLErrorKind::UnexpectedElement(name.as_owned(), elem_name.clone()),
830                    span: next_child.span(),
831                });
832                // Unknown element at this level
833                next_child.skip()?;
834            }
835        }
836
837        $( collect_struct!(@process $root, $items); )*
838
839        collect_struct!(@assign $root, $ty, [], $($items ; )*)
840    }};
841
842    // Emit field declarations
843    (@decl (@parse_only [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
844        let mut $field: Option<_> = None;
845    };
846    (@decl (@direct [$field:ident] [$value:expr])) => {};
847    (@decl (@optional [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
848        let mut $field: Option<_> = None;
849    };
850    (@decl (@btreemap [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
851        let mut $field: std::collections::BTreeMap<_, _> = std::collections::BTreeMap::new();
852    };
853    (@decl (@field [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
854        let mut $field: Option<_> = None;
855    };
856    (@decl (@vec [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
857        let mut $field: Vec<_> = Vec::new();
858    };
859
860    // Emit match arms for each field
861    (@matcher $next_child:ident, $name:ident, $handled:ident, (@direct [$field:ident] [$value:expr])) => {};
862    (@matcher $next_child:ident, $name:ident, $handled:ident, (@optional [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
863        collect_struct!(@matcher $next_child, $name, $handled, (@field [$field] [$namespaced_name] [$var] [$map]));
864    };
865    (@matcher $next_child:ident, $name:ident, $handled:ident, (@parse_only [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
866        collect_struct!(@matcher $next_child, $name, $handled, (@field [$field] [$namespaced_name] [$var] [$map]));
867    };
868    (@matcher $next_child:ident, $name:ident, $handled:ident, (@field [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
869        if !$handled &&
870            &$name == $crate::io::IntoQualifiedNameCow::into_qname_cow($namespaced_name).as_ref()
871        {
872            let $var = &mut $next_child;
873            $field = Some($map);
874            $var.skip()?;
875            $handled = true;
876        }
877    };
878    (@matcher $next_child:ident, $name:ident, $handled:ident, (@vec [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
879        if !$handled &&
880            &$name == $crate::io::IntoQualifiedNameCow::into_qname_cow($namespaced_name).as_ref()
881        {
882            let $var = &mut $next_child;
883            $field.push($map);
884            $var.skip()?;
885            $handled = true;
886        }
887    };
888    (@matcher $next_child:ident, $name:ident, $handled:ident, (@btreemap [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
889        if !$handled &&
890            &$name == $crate::io::IntoQualifiedNameCow::into_qname_cow($namespaced_name).as_ref()
891        {
892            let $var = &mut $next_child;
893            let (k, v) = $map;
894            $field.insert(k, v);
895            $var.skip()?;
896            $handled = true;
897        }
898    };
899
900    // Emit pre-processing values
901    (@process $root:expr, (@parse_only [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {
902        $crate::error::EMLResultExt::with_span(
903            $field.ok_or_else(|| $crate::error::EMLErrorKind::MissingElement(
904                $crate::io::QualifiedName::from($namespaced_name).as_owned()
905            )),
906            $root.last_span()
907        )?;
908    };
909    (@process $root:expr, (@direct [$field:ident] [$value:expr])) => {};
910    (@process $root:expr, (@optional [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {};
911    (@process $root:expr, (@btreemap [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {};
912    (@process $root:expr, (@field [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {};
913    (@process $root:expr, (@vec [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr])) => {};
914
915    // Start emitting field assignments
916    (@build_struct $root:expr, $ty:ident, $($items:tt ; )* ) => {
917        $ty {
918            collect_struct!(@assign $root, $($items ; )*)
919        }
920    };
921
922    // Emit struct field assignments
923    (@assign $root:expr, $ty:ident, [$($out:tt)*], (@direct [$field:ident] [$value:expr]) ; $($tail:tt)*) => {
924        collect_struct!(@assign $root, $ty, [
925            $($out)*
926            $field: $value,
927        ], $($tail)*)
928    };
929    (@assign $root:expr, $ty:ident, [$($out:tt)*], (@parse_only [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr]) ; $($tail:tt)*) => {
930        collect_struct!(@assign $root, $ty, [
931            $($out)*
932        ], $($tail)*)
933    };
934    (@assign $root:expr, $ty:ident, [$($out:tt)*], (@optional [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr]) ; $($tail:tt)*) => {
935        collect_struct!(@assign $root, $ty, [
936            $($out)*
937            $field: $field,
938        ], $($tail)*)
939    };
940    (@assign $root:expr, $ty:ident, [$($out:tt)*], (@vec [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr]) ; $($tail:tt)*) => {
941        collect_struct!(@assign $root, $ty, [
942            $($out)*
943            $field: $field,
944        ], $($tail)*)
945    };
946    (@assign $root:expr, $ty:ident, [$($out:tt)*], (@btreemap [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr]) ; $($tail:tt)*) => {
947        collect_struct!(@assign $root, $ty, [
948            $($out)*
949            $field: $field,
950        ], $($tail)*)
951    };
952    (@assign $root:expr, $ty:ident, [$($out:tt)*], (@field [$field:ident] [$namespaced_name:expr] [$var:ident] [$map:expr]) ; $($tail:tt)*) => {
953        collect_struct!(@assign $root, $ty, [
954            $($out)*
955            $field: $crate::error::EMLResultExt::with_span(
956                $field.ok_or_else(|| $crate::error::EMLErrorKind::MissingElement(
957                    $crate::io::QualifiedName::from($namespaced_name).as_owned()
958                )),
959                $root.last_span()
960            )?,
961        ], $($tail)*)
962    };
963    (@assign $root:expr, $ty:ident, [$($out:tt)*], ) => {
964        $ty {
965            $($out)*
966        }
967    };
968}
969pub(crate) use collect_struct;
970
971#[cfg(test)]
972mod tests {
973    use super::*;
974
975    #[test]
976    fn test_unknown_namespace() {
977        let document = r#"<eml:UnknownElement />"#;
978        let mut reader = EMLReader::init_from_str(document, EMLParsingMode::Strict);
979        let root = reader.next_element().unwrap();
980        let error = root.name().unwrap_err();
981        assert!(matches!(
982            error.kind(),
983            EMLErrorKind::UnknownNamespace(ns) if ns == "eml"
984        ));
985    }
986
987    /// Input without any XML element used to make `next_element` loop forever,
988    /// since quick-xml keeps returning `Event::Eof` (kiesraad/abacus#3582).
989    #[test]
990    fn test_input_without_element_returns_unexpected_eof() {
991        for document in [
992            "",
993            "5738 d520 a7f2 89b8 8875 ebfe cfc8 6012 d7b6 f65f 3271 d0e1 180b ccdd 8134 cd5d\n",
994            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
995        ] {
996            let mut reader = EMLReader::init_from_str(document, EMLParsingMode::Strict);
997            let Err(error) = reader.next_element() else {
998                panic!("expected an error for input {document:?}");
999            };
1000            assert!(matches!(error.kind(), EMLErrorKind::UnexpectedEof));
1001        }
1002    }
1003}