meta.rs

  1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7//! # Parse Rust attributes
  8//!
  9//! This module is concerned with parsing attributes from the Rust "meta"
 10//! annotations on structs, enums, enum variants and fields.
 11
 12use std::hash::{Hash, Hasher};
 13
 14use proc_macro2::{Span, TokenStream};
 15use quote::{quote, quote_spanned};
 16use syn::{meta::ParseNestedMeta, spanned::Spanned, *};
 17
 18use rxml_validation::NcName;
 19
 20/// XML core namespace URI (for the `xml:` prefix)
 21pub const XMLNS_XML: &str = "http://www.w3.org/XML/1998/namespace";
 22/// XML namespace URI (for the `xmlns:` prefix)
 23pub const XMLNS_XMLNS: &str = "http://www.w3.org/2000/xmlns/";
 24
 25macro_rules! reject_key {
 26    ($key:ident not on $not_allowed_on:literal only on $only_allowed_on:literal) => {
 27        if let Some($key) = $key {
 28            return Err(Error::new_spanned(
 29                $key,
 30                concat!(
 31                    "`",
 32                    stringify!($key),
 33                    "` is not allowed on ",
 34                    $not_allowed_on,
 35                    " (only on ",
 36                    $only_allowed_on,
 37                    ")"
 38                ),
 39            ));
 40        }
 41    };
 42
 43    ($key:ident flag not on $not_allowed_on:literal only on $only_allowed_on:literal) => {
 44        if let Flag::Present($key) = $key {
 45            return Err(Error::new(
 46                $key,
 47                concat!(
 48                    "`",
 49                    stringify!($key),
 50                    "` is not allowed on ",
 51                    $not_allowed_on,
 52                    " (only on ",
 53                    $only_allowed_on,
 54                    ")"
 55                ),
 56            ));
 57        }
 58    };
 59}
 60
 61pub(crate) use reject_key;
 62
 63/// Value for the `#[xml(namespace = ..)]` attribute.
 64#[derive(Debug)]
 65pub(crate) enum NamespaceRef {
 66    /// The XML namespace is specified as a string literal.
 67    LitStr(LitStr),
 68
 69    /// The XML namespace is specified as a path.
 70    Path(Path),
 71}
 72
 73impl NamespaceRef {
 74    fn fudge(value: &str, span: Span) -> Self {
 75        Self::LitStr(LitStr::new(value, span))
 76    }
 77}
 78
 79impl syn::parse::Parse for NamespaceRef {
 80    fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
 81        if input.peek(syn::LitStr) {
 82            Ok(Self::LitStr(input.parse()?))
 83        } else {
 84            Ok(Self::Path(input.parse()?))
 85        }
 86    }
 87}
 88
 89impl quote::ToTokens for NamespaceRef {
 90    fn to_tokens(&self, tokens: &mut TokenStream) {
 91        match self {
 92            Self::LitStr(ref lit) => lit.to_tokens(tokens),
 93            Self::Path(ref path) => path.to_tokens(tokens),
 94        }
 95    }
 96}
 97
 98/// Value for the `#[xml(name = .. )]` attribute.
 99#[derive(Debug, Clone)]
100pub(crate) enum NameRef {
101    /// The XML name is specified as a string literal.
102    Literal {
103        /// The validated XML name.
104        value: NcName,
105
106        /// The span of the original [`syn::LitStr`].
107        span: Span,
108    },
109
110    /// The XML name is specified as a path.
111    Path(Path),
112}
113
114impl Hash for NameRef {
115    fn hash<H: Hasher>(&self, h: &mut H) {
116        match self {
117            Self::Literal { ref value, .. } => value.hash(h),
118            Self::Path(ref path) => path.hash(h),
119        }
120    }
121}
122
123impl PartialEq for NameRef {
124    fn eq(&self, other: &NameRef) -> bool {
125        match self {
126            Self::Literal {
127                value: ref my_value,
128                ..
129            } => match other {
130                Self::Literal {
131                    value: ref other_value,
132                    ..
133                } => my_value == other_value,
134                _ => false,
135            },
136            Self::Path(ref my_path) => match other {
137                Self::Path(ref other_path) => my_path == other_path,
138                _ => false,
139            },
140        }
141    }
142}
143
144impl Eq for NameRef {}
145
146impl syn::parse::Parse for NameRef {
147    fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
148        if input.peek(syn::LitStr) {
149            let s: LitStr = input.parse()?;
150            let span = s.span();
151            match NcName::try_from(s.value()) {
152                Ok(value) => Ok(Self::Literal { value, span }),
153                Err(e) => Err(Error::new(span, format!("not a valid XML name: {}", e))),
154            }
155        } else {
156            let p: Path = input.parse()?;
157            Ok(Self::Path(p))
158        }
159    }
160}
161
162impl quote::ToTokens for NameRef {
163    fn to_tokens(&self, tokens: &mut TokenStream) {
164        match self {
165            Self::Literal { ref value, span } => {
166                let span = *span;
167                let value = value.as_str();
168                let value = quote_spanned! { span=> #value };
169                // SAFETY: self.0 is a known-good NcName, so converting it to an
170                // NcNameStr is known to be safe.
171                // NOTE: we cannot use `quote_spanned! { self.span=> }` for the unsafe
172                // block as that would then in fact trip a `#[deny(unsafe_code)]` lint
173                // at the use site of the macro.
174                tokens.extend(quote! {
175                    unsafe { ::xso::exports::rxml::NcNameStr::from_str_unchecked(#value) }
176                })
177            }
178            Self::Path(ref path) => path.to_tokens(tokens),
179        }
180    }
181}
182
183/// Represents the amount constraint used with child elements.
184///
185/// Currently, this only supports "one" (literal `1`) or "any amount" (`..`).
186/// In the future, we might want to add support for any range pattern for
187/// `usize` and any positive integer literal.
188#[derive(Debug)]
189pub(crate) enum AmountConstraint {
190    /// Equivalent to `1`
191    #[allow(dead_code)]
192    FixedSingle(Span),
193
194    /// Equivalent to `..`.
195    Any(Span),
196}
197
198impl syn::parse::Parse for AmountConstraint {
199    fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
200        if input.peek(LitInt) && !input.peek2(token::DotDot) && !input.peek2(token::DotDotEq) {
201            let lit: LitInt = input.parse()?;
202            let value: usize = lit.base10_parse()?;
203            if value == 1 {
204                Ok(Self::FixedSingle(lit.span()))
205            } else {
206                Err(Error::new(lit.span(), "only `1` and `..` are allowed here"))
207            }
208        } else {
209            let p: PatRange = input.parse()?;
210            if let Some(attr) = p.attrs.first() {
211                return Err(Error::new_spanned(attr, "attributes not allowed here"));
212            }
213            if let Some(start) = p.start.as_ref() {
214                return Err(Error::new_spanned(
215                    start,
216                    "only full ranges (`..`) are allowed here",
217                ));
218            }
219            if let Some(end) = p.end.as_ref() {
220                return Err(Error::new_spanned(
221                    end,
222                    "only full ranges (`..`) are allowed here",
223                ));
224            }
225            Ok(Self::Any(p.span()))
226        }
227    }
228}
229
230/// Represents a boolean flag from a `#[xml(..)]` attribute meta.
231#[derive(Clone, Copy, Debug)]
232pub(crate) enum Flag {
233    /// The flag is not set.
234    Absent,
235
236    /// The flag was set.
237    Present(
238        /// The span of the syntax element which enabled the flag.
239        ///
240        /// This is used to generate useful error messages by pointing at the
241        /// specific place the flag was activated.
242        #[allow(dead_code)]
243        Span,
244    ),
245}
246
247impl Flag {
248    /// Return true if the flag is set, false otherwise.
249    pub(crate) fn is_set(&self) -> bool {
250        match self {
251            Self::Absent => false,
252            Self::Present(_) => true,
253        }
254    }
255}
256
257impl<T: Spanned> From<T> for Flag {
258    fn from(other: T) -> Flag {
259        Flag::Present(other.span())
260    }
261}
262
263/// A pair of `namespace` and `name` keys.
264#[derive(Debug, Default)]
265pub(crate) struct QNameRef {
266    /// The XML namespace supplied.
267    pub(crate) namespace: Option<NamespaceRef>,
268
269    /// The XML name supplied.
270    pub(crate) name: Option<NameRef>,
271}
272
273impl QNameRef {
274    /// Attempt to incrementally parse this QNameRef.
275    ///
276    /// If `meta` contains either `namespace` or `name` keys, they are
277    /// processed and either `Ok(None)` or an error is returned.
278    ///
279    /// If no matching key is found, `Ok(Some(meta))` is returned for further
280    /// processing.
281    fn parse_incremental_from_meta<'x>(
282        &mut self,
283        meta: ParseNestedMeta<'x>,
284    ) -> Result<Option<ParseNestedMeta<'x>>> {
285        if meta.path.is_ident("name") {
286            if self.name.is_some() {
287                return Err(Error::new_spanned(meta.path, "duplicate `name` key"));
288            }
289            let value = meta.value()?;
290            let name_span = value.span();
291            let (new_namespace, new_name) = parse_prefixed_name(value)?;
292            if let Some(new_namespace) = new_namespace {
293                if let Some(namespace) = self.namespace.as_ref() {
294                    let mut error = Error::new(
295                        name_span,
296                        "cannot combine `namespace` key with prefixed `name`",
297                    );
298                    error.combine(Error::new_spanned(namespace, "`namespace` was set here"));
299                    return Err(error);
300                }
301                self.namespace = Some(new_namespace);
302            }
303            self.name = Some(new_name);
304            Ok(None)
305        } else if meta.path.is_ident("namespace") {
306            if self.namespace.is_some() {
307                return Err(Error::new_spanned(
308                    meta.path,
309                    "duplicate `namespace` key or `name` key has prefix",
310                ));
311            }
312            self.namespace = Some(meta.value()?.parse()?);
313            Ok(None)
314        } else {
315            Ok(Some(meta))
316        }
317    }
318}
319
320/// Contents of an `#[xml(..)]` attribute on a struct, enum variant, or enum.
321#[derive(Debug)]
322pub(crate) struct XmlCompoundMeta {
323    /// The span of the `#[xml(..)]` meta from which this was parsed.
324    ///
325    /// This is useful for error messages.
326    pub(crate) span: Span,
327
328    /// The value assigned to `namespace` and `name` fields inside
329    /// `#[xml(..)]`, if any.
330    pub(crate) qname: QNameRef,
331
332    /// The debug flag.
333    pub(crate) debug: Flag,
334
335    /// The value assigned to `builder` inside `#[xml(..)]`, if any.
336    pub(crate) builder: Option<Ident>,
337
338    /// The value assigned to `iterator` inside `#[xml(..)]`, if any.
339    pub(crate) iterator: Option<Ident>,
340
341    /// The exhaustive flag.
342    pub(crate) exhaustive: Flag,
343}
344
345impl XmlCompoundMeta {
346    /// Parse the meta values from a `#[xml(..)]` attribute.
347    ///
348    /// Undefined options or options with incompatible values are rejected
349    /// with an appropriate compile-time error.
350    fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
351        let mut qname = QNameRef::default();
352        let mut builder = None;
353        let mut iterator = None;
354        let mut debug = Flag::Absent;
355        let mut exhaustive = Flag::Absent;
356
357        attr.parse_nested_meta(|meta| {
358            if meta.path.is_ident("debug") {
359                if debug.is_set() {
360                    return Err(Error::new_spanned(meta.path, "duplicate `debug` key"));
361                }
362                debug = (&meta.path).into();
363                Ok(())
364            } else if meta.path.is_ident("builder") {
365                if builder.is_some() {
366                    return Err(Error::new_spanned(meta.path, "duplicate `builder` key"));
367                }
368                builder = Some(meta.value()?.parse()?);
369                Ok(())
370            } else if meta.path.is_ident("iterator") {
371                if iterator.is_some() {
372                    return Err(Error::new_spanned(meta.path, "duplicate `iterator` key"));
373                }
374                iterator = Some(meta.value()?.parse()?);
375                Ok(())
376            } else if meta.path.is_ident("exhaustive") {
377                if exhaustive.is_set() {
378                    return Err(Error::new_spanned(meta.path, "duplicate `exhaustive` key"));
379                }
380                exhaustive = (&meta.path).into();
381                Ok(())
382            } else {
383                match qname.parse_incremental_from_meta(meta)? {
384                    None => Ok(()),
385                    Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")),
386                }
387            }
388        })?;
389
390        Ok(Self {
391            span: attr.span(),
392            qname,
393            debug,
394            builder,
395            iterator,
396            exhaustive,
397        })
398    }
399
400    /// Search through `attrs` for a single `#[xml(..)]` attribute and parse
401    /// it.
402    ///
403    /// Undefined options or options with incompatible values are rejected
404    /// with an appropriate compile-time error.
405    ///
406    /// If more than one `#[xml(..)]` attribute is found, an error is
407    /// emitted.
408    ///
409    /// If no `#[xml(..)]` attribute is found, `None` is returned.
410    pub(crate) fn try_parse_from_attributes(attrs: &[Attribute]) -> Result<Option<Self>> {
411        let mut result = None;
412        for attr in attrs {
413            if !attr.path().is_ident("xml") {
414                continue;
415            }
416            if result.is_some() {
417                return Err(syn::Error::new_spanned(
418                    attr.path(),
419                    "only one #[xml(..)] per struct or enum variant allowed",
420                ));
421            }
422            result = Some(Self::parse_from_attribute(attr)?);
423        }
424        Ok(result)
425    }
426
427    /// Search through `attrs` for a single `#[xml(..)]` attribute and parse
428    /// it.
429    ///
430    /// Undefined options or options with incompatible values are rejected
431    /// with an appropriate compile-time error.
432    ///
433    /// If more than one or no `#[xml(..)]` attribute is found, an error is
434    /// emitted.
435    pub(crate) fn parse_from_attributes(attrs: &[Attribute]) -> Result<Self> {
436        match Self::try_parse_from_attributes(attrs)? {
437            Some(v) => Ok(v),
438            None => Err(syn::Error::new(
439                Span::call_site(),
440                "#[xml(..)] attribute required on struct or enum variant",
441            )),
442        }
443    }
444}
445
446/// Return true if the tokens the cursor points at are a valid type path
447/// prefix.
448///
449/// This does not advance the parse stream.
450///
451/// If the tokens *do* look like a type path, a Span which points at the first
452/// `<` encountered is returned. This can be used for a helpful error message
453/// in case parsing the type path does then fail.
454fn maybe_type_path(p: parse::ParseStream<'_>) -> (bool, Option<Span>) {
455    // ParseStream cursors do not advance the stream, but they are also rather
456    // unwieldly to use. Prepare for a lot of `let .. = ..`.
457
458    let cursor = if p.peek(token::PathSep) {
459        // If we have a path separator, we need to skip that initially. We
460        // do this by skipping two punctuations. We use unwrap() here because
461        // we already know for sure that we see two punctuation items (because
462        // of the peek).
463        p.cursor().punct().unwrap().1.punct().unwrap().1
464    } else {
465        // No `::` initially, so we just take what we have.
466        p.cursor()
467    };
468
469    // Now we loop over `$ident::` segments. If we find anything but a `:`
470    // after the ident, we exit. Depending on *what* we find, we either exit
471    // true or false, but see for yourself.
472    let mut cursor = cursor;
473    loop {
474        // Here we look for the identifier, but we do not care for its
475        // contents.
476        let Some((_, new_cursor)) = cursor.ident() else {
477            return (false, None);
478        };
479        cursor = new_cursor;
480
481        // Now we see what actually follows the ident (it must be punctuation
482        // for it to be a type path...)
483        let Some((punct, new_cursor)) = cursor.punct() else {
484            return (false, None);
485        };
486        cursor = new_cursor;
487
488        match punct.as_char() {
489            // Looks like a `foo<..`, we treat that as a type path for the
490            // reasons stated in [`parse_codec_expr`]'s doc.
491            '<' => return (true, Some(punct.span())),
492
493            // Continue looking ahead: looks like a path separator.
494            ':' => (),
495
496            // Anything else (such as `,` (separating another argument most
497            // likely), or `.` (a method call?)) we treat as "not a type
498            // path".
499            _ => return (false, None),
500        }
501
502        // If we are here, we saw a `:`. Look for the second one.
503        let Some((punct, new_cursor)) = cursor.punct() else {
504            return (false, None);
505        };
506        cursor = new_cursor;
507
508        if punct.as_char() != ':' {
509            // If it is not another `:`, it cannot be a type path.
510            return (false, None);
511        }
512
513        // And round and round and round it goes.
514        // We will terminate eventually because the cursor will return None
515        // on any of the lookups because parse streams are (hopefully!)
516        // finite. Most likely, we'll however encounter a `<` or other non-`:`
517        // punctuation first.
518    }
519}
520
521/// Parse expressions passed to `codec`.
522///
523/// Those will generally be paths to unit type constructors (such as `Foo`)
524/// or references to static values or chains of function calls.
525///
526/// In the case of unit type constructors for generic types, users may type
527/// for example `FixedHex<20>`, thinking they are writing a type path. However,
528/// while `FixedHex<20>` is indeed a valid type path, it is not a valid
529/// expression for a unit type constructor. Instead it is parsed as
530/// `FixedHex < 20` and then a syntax error.
531///
532/// We however know that `Foo < Bar` is never a valid expression for a type.
533/// Thus, we can be smart about this and inject the `::` at the right place
534/// automatically.
535fn parse_codec_expr(p: parse::ParseStream<'_>) -> Result<(Expr, Option<Error>)> {
536    let (maybe_type_path, punct_span) = maybe_type_path(p);
537    if maybe_type_path {
538        let helpful_error =
539            punct_span.map(|span| Error::new(span, "help: try inserting a `::` before this `<`"));
540        let mut type_path: TypePath = match p.parse() {
541            Ok(v) => v,
542            Err(mut e) => match helpful_error {
543                Some(help) => {
544                    e.combine(help);
545                    return Err(e);
546                }
547                None => return Err(e),
548            },
549        };
550        // We got a type path -- so we now inject the `::` before any `<` as
551        // needed.
552        for segment in type_path.path.segments.iter_mut() {
553            match segment.arguments {
554                PathArguments::AngleBracketed(ref mut arguments) => {
555                    let span = arguments.span();
556                    arguments
557                        .colon2_token
558                        .get_or_insert_with(|| token::PathSep {
559                            spans: [span, span],
560                        });
561                }
562                _ => (),
563            }
564        }
565        Ok((
566            Expr::Path(ExprPath {
567                attrs: Vec::new(),
568                qself: type_path.qself,
569                path: type_path.path,
570            }),
571            helpful_error,
572        ))
573    } else {
574        p.parse().map(|x| (x, None))
575    }
576}
577
578/// Parse an XML name while resolving built-in namespace prefixes.
579fn parse_prefixed_name(
580    value: syn::parse::ParseStream<'_>,
581) -> Result<(Option<NamespaceRef>, NameRef)> {
582    if !value.peek(LitStr) {
583        // if we don't have a string literal next, we delegate to the default
584        // `NameRef` parser.
585        return Ok((None, value.parse()?));
586    }
587
588    let name: LitStr = value.parse()?;
589    let name_span = name.span();
590    let (prefix, name) = match name
591        .value()
592        .try_into()
593        .and_then(|name: rxml_validation::Name| name.split_name())
594    {
595        Ok(v) => v,
596        Err(e) => {
597            return Err(Error::new(
598                name_span,
599                format!("not a valid XML name: {}", e),
600            ))
601        }
602    };
603    let name = NameRef::Literal {
604        value: name,
605        span: name_span,
606    };
607    if let Some(prefix) = prefix {
608        let namespace_uri = match prefix.as_str() {
609            "xml" => XMLNS_XML,
610            "xmlns" => XMLNS_XMLNS,
611            other => return Err(Error::new(
612                name_span,
613                format!("prefix `{}` is not a built-in prefix and cannot be used. specify the desired namespace using the `namespace` key instead.", other)
614            )),
615        };
616        Ok((Some(NamespaceRef::fudge(namespace_uri, name_span)), name))
617    } else {
618        Ok((None, name))
619    }
620}
621
622/// Contents of an `#[xml(..)]` attribute on a struct or enum variant member.
623#[derive(Debug)]
624pub(crate) enum XmlFieldMeta {
625    /// `#[xml(attribute)]`, `#[xml(attribute = ..)]` or `#[xml(attribute(..))]`
626    Attribute {
627        /// The span of the `#[xml(attribute)]` meta from which this was parsed.
628        ///
629        /// This is useful for error messages.
630        span: Span,
631
632        /// The namespace/name keys.
633        qname: QNameRef,
634
635        /// The `default` flag.
636        default_: Flag,
637    },
638
639    /// `#[xml(text)]`
640    Text {
641        /// The path to the optional codec type.
642        codec: Option<Expr>,
643    },
644
645    /// `#[xml(child)`
646    Child {
647        /// The `default` flag.
648        default_: Flag,
649
650        /// The `n` flag.
651        amount: Option<AmountConstraint>,
652    },
653}
654
655impl XmlFieldMeta {
656    /// Parse a `#[xml(attribute(..))]` meta.
657    ///
658    /// That meta can have three distinct syntax styles:
659    /// - argument-less: `#[xml(attribute)]`
660    /// - shorthand: `#[xml(attribute = ..)]`
661    /// - full: `#[xml(attribute(..))]`
662    fn attribute_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
663        if meta.input.peek(Token![=]) {
664            // shorthand syntax
665            let (namespace, name) = parse_prefixed_name(meta.value()?)?;
666            Ok(Self::Attribute {
667                span: meta.path.span(),
668                qname: QNameRef {
669                    name: Some(name),
670                    namespace,
671                },
672                default_: Flag::Absent,
673            })
674        } else if meta.input.peek(syn::token::Paren) {
675            // full syntax
676            let mut qname = QNameRef::default();
677            let mut default_ = Flag::Absent;
678            meta.parse_nested_meta(|meta| {
679                if meta.path.is_ident("default") {
680                    if default_.is_set() {
681                        return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
682                    }
683                    default_ = (&meta.path).into();
684                    Ok(())
685                } else {
686                    match qname.parse_incremental_from_meta(meta)? {
687                        None => Ok(()),
688                        Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")),
689                    }
690                }
691            })?;
692            Ok(Self::Attribute {
693                span: meta.path.span(),
694                qname,
695                default_,
696            })
697        } else {
698            // argument-less syntax
699            Ok(Self::Attribute {
700                span: meta.path.span(),
701                qname: QNameRef::default(),
702                default_: Flag::Absent,
703            })
704        }
705    }
706
707    /// Parse a `#[xml(text)]` meta.
708    fn text_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
709        if meta.input.peek(Token![=]) {
710            let (codec, helpful_error) = parse_codec_expr(meta.value()?)?;
711            // A meta value can only be followed by either a `,`, or the end
712            // of the parse stream (because of the delimited group ending).
713            // Hence we check we are there. And if we are *not* there, we emit
714            // an error straight away, with the helpful addition from the
715            // `parse_codec_expr` if we have it.
716            //
717            // If we do not do this, the user gets a rather confusing
718            // "expected `,`" message if the `maybe_type_path` guess was
719            // wrong.
720            let lookahead = meta.input.lookahead1();
721            if !lookahead.peek(Token![,]) && !meta.input.is_empty() {
722                if let Some(helpful_error) = helpful_error {
723                    let mut e = lookahead.error();
724                    e.combine(helpful_error);
725                    return Err(e);
726                }
727            }
728            Ok(Self::Text { codec: Some(codec) })
729        } else if meta.input.peek(syn::token::Paren) {
730            let mut codec: Option<Expr> = None;
731            meta.parse_nested_meta(|meta| {
732                if meta.path.is_ident("codec") {
733                    if codec.is_some() {
734                        return Err(Error::new_spanned(meta.path, "duplicate `codec` key"));
735                    }
736                    let (new_codec, helpful_error) = parse_codec_expr(meta.value()?)?;
737                    // See above (at the top-ish of this function) for why we
738                    // do this.
739                    let lookahead = meta.input.lookahead1();
740                    if !lookahead.peek(Token![,]) && !meta.input.is_empty() {
741                        if let Some(helpful_error) = helpful_error {
742                            let mut e = lookahead.error();
743                            e.combine(helpful_error);
744                            return Err(e);
745                        }
746                    }
747                    codec = Some(new_codec);
748                    Ok(())
749                } else {
750                    Err(Error::new_spanned(meta.path, "unsupported key"))
751                }
752            })?;
753            Ok(Self::Text { codec })
754        } else {
755            Ok(Self::Text { codec: None })
756        }
757    }
758
759    /// Parse a `#[xml(child)]` meta.
760    fn child_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
761        if meta.input.peek(syn::token::Paren) {
762            let mut default_ = Flag::Absent;
763            let mut amount = None;
764            meta.parse_nested_meta(|meta| {
765                if meta.path.is_ident("default") {
766                    if default_.is_set() {
767                        return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
768                    }
769                    default_ = (&meta.path).into();
770                    Ok(())
771                } else if meta.path.is_ident("n") {
772                    if amount.is_some() {
773                        return Err(Error::new_spanned(meta.path, "duplicate `n` key"));
774                    }
775                    amount = Some(meta.value()?.parse()?);
776                    Ok(())
777                } else {
778                    Err(Error::new_spanned(meta.path, "unsupported key"))
779                }
780            })?;
781            Ok(Self::Child { default_, amount })
782        } else {
783            Ok(Self::Child {
784                default_: Flag::Absent,
785                amount: None,
786            })
787        }
788    }
789
790    /// Parse [`Self`] from a nestd meta, switching on the identifier
791    /// of that nested meta.
792    fn parse_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
793        if meta.path.is_ident("attribute") {
794            Self::attribute_from_meta(meta)
795        } else if meta.path.is_ident("text") {
796            Self::text_from_meta(meta)
797        } else if meta.path.is_ident("child") {
798            Self::child_from_meta(meta)
799        } else {
800            Err(Error::new_spanned(meta.path, "unsupported field meta"))
801        }
802    }
803
804    /// Parse an `#[xml(..)]` meta on a field.
805    ///
806    /// This switches based on the first identifier within the `#[xml(..)]`
807    /// meta and generates an enum variant accordingly.
808    ///
809    /// Only a single nested meta is allowed; more than one will be
810    /// rejected with an appropriate compile-time error.
811    ///
812    /// If no meta is contained at all, a compile-time error is generated.
813    ///
814    /// Undefined options or options with incompatible values are rejected
815    /// with an appropriate compile-time error.
816    pub(crate) fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
817        let mut result: Option<Self> = None;
818
819        attr.parse_nested_meta(|meta| {
820            if result.is_some() {
821                return Err(Error::new_spanned(
822                    meta.path,
823                    "multiple field type specifiers are not supported",
824                ));
825            }
826
827            result = Some(Self::parse_from_meta(meta)?);
828            Ok(())
829        })?;
830
831        if let Some(result) = result {
832            Ok(result)
833        } else {
834            Err(Error::new_spanned(
835                attr,
836                "missing field type specifier within `#[xml(..)]`",
837            ))
838        }
839    }
840
841    /// Find and parse a `#[xml(..)]` meta on a field.
842    ///
843    /// This invokes [`Self::parse_from_attribute`] internally on the first
844    /// encountered `#[xml(..)]` meta.
845    ///
846    /// If not exactly one `#[xml(..)]` meta is encountered, an error is
847    /// returned. The error is spanned to `err_span`.
848    pub(crate) fn parse_from_attributes(attrs: &[Attribute], err_span: &Span) -> Result<Self> {
849        let mut result: Option<Self> = None;
850        for attr in attrs {
851            if !attr.path().is_ident("xml") {
852                continue;
853            }
854
855            if result.is_some() {
856                return Err(Error::new_spanned(
857                    attr,
858                    "only one #[xml(..)] attribute per field allowed.",
859                ));
860            }
861
862            result = Some(Self::parse_from_attribute(attr)?);
863        }
864
865        if let Some(result) = result {
866            Ok(result)
867        } else {
868            Err(Error::new(*err_span, "missing #[xml(..)] meta on field"))
869        }
870    }
871}