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