xso_proc/
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 core::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(ref $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                    $(
36                        " (only on ",
37                        $only_allowed_on,
38                        ")",
39                    )?
40                ),
41            ));
42        }
43    };
44
45    ($key:ident flag not on $not_allowed_on:literal $(only on $only_allowed_on:literal)?) => {
46        if let Flag::Present(ref $key) = $key {
47            return Err(Error::new(
48                *$key,
49                concat!(
50                    "`",
51                    stringify!($key),
52                    "` is not allowed on ",
53                    $not_allowed_on,
54                    $(
55                        " (only on ",
56                        $only_allowed_on,
57                        ")",
58                    )?
59                ),
60            ));
61        }
62    };
63}
64
65pub(crate) use reject_key;
66
67/// Value for the `#[xml(namespace = ..)]` attribute.
68#[derive(Debug, Clone)]
69pub(crate) enum NamespaceRef {
70    /// The XML namespace is specified as a string literal.
71    LitStr(LitStr),
72
73    /// The XML namespace is specified as a path.
74    Path(Path),
75}
76
77impl NamespaceRef {
78    fn fudge(value: &str, span: Span) -> Self {
79        Self::LitStr(LitStr::new(value, span))
80    }
81}
82
83impl syn::parse::Parse for NamespaceRef {
84    fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
85        if input.peek(syn::LitStr) {
86            Ok(Self::LitStr(input.parse()?))
87        } else {
88            Ok(Self::Path(input.parse()?))
89        }
90    }
91}
92
93impl quote::ToTokens for NamespaceRef {
94    fn to_tokens(&self, tokens: &mut TokenStream) {
95        match self {
96            Self::LitStr(ref lit) => lit.to_tokens(tokens),
97            Self::Path(ref path) => path.to_tokens(tokens),
98        }
99    }
100}
101
102/// Value for the `#[xml(name = .. )]` attribute.
103#[derive(Debug, Clone)]
104pub(crate) enum NameRef {
105    /// The XML name is specified as a string literal.
106    Literal {
107        /// The validated XML name.
108        value: NcName,
109
110        /// The span of the original [`syn::LitStr`].
111        span: Span,
112    },
113
114    /// The XML name is specified as a path.
115    Path(Path),
116}
117
118impl Hash for NameRef {
119    fn hash<H: Hasher>(&self, h: &mut H) {
120        match self {
121            Self::Literal { ref value, .. } => value.hash(h),
122            Self::Path(ref path) => path.hash(h),
123        }
124    }
125}
126
127impl PartialEq for NameRef {
128    fn eq(&self, other: &NameRef) -> bool {
129        match self {
130            Self::Literal {
131                value: ref my_value,
132                ..
133            } => match other {
134                Self::Literal {
135                    value: ref other_value,
136                    ..
137                } => my_value == other_value,
138                _ => false,
139            },
140            Self::Path(ref my_path) => match other {
141                Self::Path(ref other_path) => my_path == other_path,
142                _ => false,
143            },
144        }
145    }
146}
147
148impl Eq for NameRef {}
149
150impl syn::parse::Parse for NameRef {
151    fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
152        if input.peek(syn::LitStr) {
153            let s: LitStr = input.parse()?;
154            let span = s.span();
155            match NcName::try_from(s.value()) {
156                Ok(value) => Ok(Self::Literal { value, span }),
157                Err(e) => Err(Error::new(span, format!("not a valid XML name: {}", e))),
158            }
159        } else {
160            let p: Path = input.parse()?;
161            Ok(Self::Path(p))
162        }
163    }
164}
165
166impl quote::ToTokens for NameRef {
167    fn to_tokens(&self, tokens: &mut TokenStream) {
168        match self {
169            Self::Literal { ref value, span } => {
170                let span = *span;
171                let value = value.as_str();
172                let value = quote_spanned! { span=> #value };
173                // SAFETY: self.0 is a known-good NcName, so converting it to an
174                // NcNameStr is known to be safe.
175                // NOTE: we cannot use `quote_spanned! { self.span=> }` for the unsafe
176                // block as that would then in fact trip a `#[deny(unsafe_code)]` lint
177                // at the use site of the macro.
178                tokens.extend(quote! {
179                    unsafe { ::xso::exports::rxml::NcNameStr::from_str_unchecked(#value) }
180                })
181            }
182            Self::Path(ref path) => path.to_tokens(tokens),
183        }
184    }
185}
186
187/// Represents the amount constraint used with child elements.
188///
189/// Currently, this only supports "one" (literal `1`) or "any amount" (`..`).
190/// In the future, we might want to add support for any range pattern for
191/// `usize` and any positive integer literal.
192#[derive(Debug)]
193pub(crate) enum AmountConstraint {
194    /// Equivalent to `1`
195    #[allow(dead_code)]
196    FixedSingle(Span),
197
198    /// Equivalent to `..`.
199    Any(Span),
200}
201
202impl syn::parse::Parse for AmountConstraint {
203    fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
204        if input.peek(LitInt) && !input.peek2(token::DotDot) && !input.peek2(token::DotDotEq) {
205            let lit: LitInt = input.parse()?;
206            let value: usize = lit.base10_parse()?;
207            if value == 1 {
208                Ok(Self::FixedSingle(lit.span()))
209            } else {
210                Err(Error::new(lit.span(), "only `1` and `..` are allowed here"))
211            }
212        } else {
213            let p: PatRange = input.parse()?;
214            if let Some(attr) = p.attrs.first() {
215                return Err(Error::new_spanned(attr, "attributes not allowed here"));
216            }
217            if let Some(start) = p.start.as_ref() {
218                return Err(Error::new_spanned(
219                    start,
220                    "only full ranges (`..`) are allowed here",
221                ));
222            }
223            if let Some(end) = p.end.as_ref() {
224                return Err(Error::new_spanned(
225                    end,
226                    "only full ranges (`..`) are allowed here",
227                ));
228            }
229            Ok(Self::Any(p.span()))
230        }
231    }
232}
233
234/// Represents a boolean flag from a `#[xml(..)]` attribute meta.
235#[derive(Clone, Copy, Debug)]
236pub(crate) enum Flag {
237    /// The flag is not set.
238    Absent,
239
240    /// The flag was set.
241    Present(
242        /// The span of the syntax element which enabled the flag.
243        ///
244        /// This is used to generate useful error messages by pointing at the
245        /// specific place the flag was activated.
246        #[allow(dead_code)]
247        Span,
248    ),
249}
250
251impl Flag {
252    /// Return true if the flag is set, false otherwise.
253    pub(crate) fn is_set(&self) -> bool {
254        match self {
255            Self::Absent => false,
256            Self::Present(_) => true,
257        }
258    }
259
260    /// Like `Option::take`, but for flags.
261    pub(crate) fn take(&mut self) -> Self {
262        let mut result = Flag::Absent;
263        core::mem::swap(&mut result, self);
264        result
265    }
266}
267
268impl<T: Spanned> From<T> for Flag {
269    fn from(other: T) -> Flag {
270        Flag::Present(other.span())
271    }
272}
273
274/// A pair of `namespace` and `name` keys.
275#[derive(Debug, Default)]
276pub(crate) struct QNameRef {
277    /// The XML namespace supplied.
278    pub(crate) namespace: Option<NamespaceRef>,
279
280    /// The XML name supplied.
281    pub(crate) name: Option<NameRef>,
282}
283
284impl QNameRef {
285    /// Attempt to incrementally parse this QNameRef.
286    ///
287    /// If `meta` contains either `namespace` or `name` keys, they are
288    /// processed and either `Ok(None)` or an error is returned.
289    ///
290    /// If no matching key is found, `Ok(Some(meta))` is returned for further
291    /// processing.
292    fn parse_incremental_from_meta<'x>(
293        &mut self,
294        meta: ParseNestedMeta<'x>,
295    ) -> Result<Option<ParseNestedMeta<'x>>> {
296        if meta.path.is_ident("name") {
297            if self.name.is_some() {
298                return Err(Error::new_spanned(meta.path, "duplicate `name` key"));
299            }
300            let value = meta.value()?;
301            let name_span = value.span();
302            let (new_namespace, new_name) = parse_prefixed_name(value)?;
303            if let Some(new_namespace) = new_namespace {
304                if let Some(namespace) = self.namespace.as_ref() {
305                    let mut error = Error::new(
306                        name_span,
307                        "cannot combine `namespace` key with prefixed `name`",
308                    );
309                    error.combine(Error::new_spanned(namespace, "`namespace` was set here"));
310                    return Err(error);
311                }
312                self.namespace = Some(new_namespace);
313            }
314            self.name = Some(new_name);
315            Ok(None)
316        } else if meta.path.is_ident("namespace") {
317            if self.namespace.is_some() {
318                return Err(Error::new_spanned(
319                    meta.path,
320                    "duplicate `namespace` key or `name` key has prefix",
321                ));
322            }
323            self.namespace = Some(meta.value()?.parse()?);
324            Ok(None)
325        } else {
326            Ok(Some(meta))
327        }
328    }
329}
330
331/// Contents of an `#[xml(..)]` attribute on a struct, enum variant, or enum.
332#[derive(Debug)]
333pub(crate) struct XmlCompoundMeta {
334    /// The span of the `#[xml(..)]` meta from which this was parsed.
335    ///
336    /// This is useful for error messages.
337    pub(crate) span: Span,
338
339    /// The value assigned to `namespace` and `name` fields inside
340    /// `#[xml(..)]`, if any.
341    pub(crate) qname: QNameRef,
342
343    /// The debug flag.
344    pub(crate) debug: Flag,
345
346    /// The value assigned to `builder` inside `#[xml(..)]`, if any.
347    pub(crate) builder: Option<Ident>,
348
349    /// The value assigned to `iterator` inside `#[xml(..)]`, if any.
350    pub(crate) iterator: Option<Ident>,
351
352    /// The value assigned to `on_unknown_attribute` inside `#[xml(..)]`, if
353    /// any.
354    pub(crate) on_unknown_attribute: Option<Ident>,
355
356    /// The value assigned to `on_unknown_child` inside `#[xml(..)]`, if
357    /// any.
358    pub(crate) on_unknown_child: Option<Ident>,
359
360    /// The exhaustive flag.
361    pub(crate) exhaustive: Flag,
362
363    /// The transparent flag.
364    pub(crate) transparent: Flag,
365}
366
367impl XmlCompoundMeta {
368    /// Parse the meta values from a `#[xml(..)]` attribute.
369    ///
370    /// Undefined options or options with incompatible values are rejected
371    /// with an appropriate compile-time error.
372    fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
373        let mut qname = QNameRef::default();
374        let mut builder = None;
375        let mut iterator = None;
376        let mut on_unknown_attribute = None;
377        let mut on_unknown_child = None;
378        let mut debug = Flag::Absent;
379        let mut exhaustive = Flag::Absent;
380        let mut transparent = Flag::Absent;
381
382        attr.parse_nested_meta(|meta| {
383            if meta.path.is_ident("debug") {
384                if debug.is_set() {
385                    return Err(Error::new_spanned(meta.path, "duplicate `debug` key"));
386                }
387                debug = (&meta.path).into();
388                Ok(())
389            } else if meta.path.is_ident("builder") {
390                if builder.is_some() {
391                    return Err(Error::new_spanned(meta.path, "duplicate `builder` key"));
392                }
393                builder = Some(meta.value()?.parse()?);
394                Ok(())
395            } else if meta.path.is_ident("iterator") {
396                if iterator.is_some() {
397                    return Err(Error::new_spanned(meta.path, "duplicate `iterator` key"));
398                }
399                iterator = Some(meta.value()?.parse()?);
400                Ok(())
401            } else if meta.path.is_ident("on_unknown_attribute") {
402                if on_unknown_attribute.is_some() {
403                    return Err(Error::new_spanned(
404                        meta.path,
405                        "duplicate `on_unknown_attribute` key",
406                    ));
407                }
408                on_unknown_attribute = Some(meta.value()?.parse()?);
409                Ok(())
410            } else if meta.path.is_ident("on_unknown_child") {
411                if on_unknown_child.is_some() {
412                    return Err(Error::new_spanned(
413                        meta.path,
414                        "duplicate `on_unknown_child` key",
415                    ));
416                }
417                on_unknown_child = Some(meta.value()?.parse()?);
418                Ok(())
419            } else if meta.path.is_ident("exhaustive") {
420                if exhaustive.is_set() {
421                    return Err(Error::new_spanned(meta.path, "duplicate `exhaustive` key"));
422                }
423                exhaustive = (&meta.path).into();
424                Ok(())
425            } else if meta.path.is_ident("transparent") {
426                if transparent.is_set() {
427                    return Err(Error::new_spanned(meta.path, "duplicate `transparent` key"));
428                }
429                transparent = (&meta.path).into();
430                Ok(())
431            } else {
432                match qname.parse_incremental_from_meta(meta)? {
433                    None => Ok(()),
434                    Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")),
435                }
436            }
437        })?;
438
439        Ok(Self {
440            span: attr.span(),
441            qname,
442            debug,
443            builder,
444            iterator,
445            on_unknown_attribute,
446            on_unknown_child,
447            exhaustive,
448            transparent,
449        })
450    }
451
452    /// Search through `attrs` for a single `#[xml(..)]` attribute and parse
453    /// it.
454    ///
455    /// Undefined options or options with incompatible values are rejected
456    /// with an appropriate compile-time error.
457    ///
458    /// If more than one `#[xml(..)]` attribute is found, an error is
459    /// emitted.
460    ///
461    /// If no `#[xml(..)]` attribute is found, `None` is returned.
462    pub(crate) fn try_parse_from_attributes(attrs: &[Attribute]) -> Result<Option<Self>> {
463        let mut result = None;
464        for attr in attrs {
465            if !attr.path().is_ident("xml") {
466                continue;
467            }
468            if result.is_some() {
469                return Err(syn::Error::new_spanned(
470                    attr.path(),
471                    "only one #[xml(..)] per struct or enum variant allowed",
472                ));
473            }
474            result = Some(Self::parse_from_attribute(attr)?);
475        }
476        Ok(result)
477    }
478
479    /// Search through `attrs` for a single `#[xml(..)]` attribute and parse
480    /// it.
481    ///
482    /// Undefined options or options with incompatible values are rejected
483    /// with an appropriate compile-time error.
484    ///
485    /// If more than one or no `#[xml(..)]` attribute is found, an error is
486    /// emitted.
487    pub(crate) fn parse_from_attributes(attrs: &[Attribute]) -> Result<Self> {
488        match Self::try_parse_from_attributes(attrs)? {
489            Some(v) => Ok(v),
490            None => Err(syn::Error::new(
491                Span::call_site(),
492                "#[xml(..)] attribute required on struct or enum variant",
493            )),
494        }
495    }
496}
497
498/// Return true if the tokens the cursor points at are a valid type path
499/// prefix.
500///
501/// This does not advance the parse stream.
502///
503/// If the tokens *do* look like a type path, a Span which points at the first
504/// `<` encountered is returned. This can be used for a helpful error message
505/// in case parsing the type path does then fail.
506fn maybe_type_path(p: parse::ParseStream<'_>) -> (bool, Option<Span>) {
507    // ParseStream cursors do not advance the stream, but they are also rather
508    // unwieldy to use. Prepare for a lot of `let .. = ..`.
509
510    let cursor = if p.peek(token::PathSep) {
511        // If we have a path separator, we need to skip that initially. We
512        // do this by skipping two punctuations. We use unwrap() here because
513        // we already know for sure that we see two punctuation items (because
514        // of the peek).
515        p.cursor().punct().unwrap().1.punct().unwrap().1
516    } else {
517        // No `::` initially, so we just take what we have.
518        p.cursor()
519    };
520
521    // Now we loop over `$ident::` segments. If we find anything but a `:`
522    // after the ident, we exit. Depending on *what* we find, we either exit
523    // true or false, but see for yourself.
524    let mut cursor = cursor;
525    loop {
526        // Here we look for the identifier, but we do not care for its
527        // contents.
528        let Some((_, new_cursor)) = cursor.ident() else {
529            return (false, None);
530        };
531        cursor = new_cursor;
532
533        // Now we see what actually follows the ident (it must be punctuation
534        // for it to be a type path...)
535        let Some((punct, new_cursor)) = cursor.punct() else {
536            return (false, None);
537        };
538        cursor = new_cursor;
539
540        match punct.as_char() {
541            // Looks like a `foo<..`, we treat that as a type path for the
542            // reasons stated in [`parse_codec_expr`]'s doc.
543            '<' => return (true, Some(punct.span())),
544
545            // Continue looking ahead: looks like a path separator.
546            ':' => (),
547
548            // Anything else (such as `,` (separating another argument most
549            // likely), or `.` (a method call?)) we treat as "not a type
550            // path".
551            _ => return (false, None),
552        }
553
554        // If we are here, we saw a `:`. Look for the second one.
555        let Some((punct, new_cursor)) = cursor.punct() else {
556            return (false, None);
557        };
558        cursor = new_cursor;
559
560        if punct.as_char() != ':' {
561            // If it is not another `:`, it cannot be a type path.
562            return (false, None);
563        }
564
565        // And round and round and round it goes.
566        // We will terminate eventually because the cursor will return None
567        // on any of the lookups because parse streams are (hopefully!)
568        // finite. Most likely, we'll however encounter a `<` or other non-`:`
569        // punctuation first.
570    }
571}
572
573/// Parse expressions passed to `codec`.
574///
575/// Those will generally be paths to unit type constructors (such as `Foo`)
576/// or references to static values or chains of function calls.
577///
578/// In the case of unit type constructors for generic types, users may type
579/// for example `FixedHex<20>`, thinking they are writing a type path. However,
580/// while `FixedHex<20>` is indeed a valid type path, it is not a valid
581/// expression for a unit type constructor. Instead it is parsed as
582/// `FixedHex < 20` and then a syntax error.
583///
584/// We however know that `Foo < Bar` is never a valid expression for a type.
585/// Thus, we can be smart about this and inject the `::` at the right place
586/// automatically.
587fn parse_codec_expr(p: parse::ParseStream<'_>) -> Result<(Expr, Option<Error>)> {
588    let (maybe_type_path, punct_span) = maybe_type_path(p);
589    if maybe_type_path {
590        let helpful_error =
591            punct_span.map(|span| Error::new(span, "help: try inserting a `::` before this `<`"));
592        let mut type_path: TypePath = match p.parse() {
593            Ok(v) => v,
594            Err(mut e) => match helpful_error {
595                Some(help) => {
596                    e.combine(help);
597                    return Err(e);
598                }
599                None => return Err(e),
600            },
601        };
602        // We got a type path -- so we now inject the `::` before any `<` as
603        // needed.
604        for segment in type_path.path.segments.iter_mut() {
605            match segment.arguments {
606                PathArguments::AngleBracketed(ref mut arguments) => {
607                    let span = arguments.span();
608                    arguments
609                        .colon2_token
610                        .get_or_insert_with(|| token::PathSep {
611                            spans: [span, span],
612                        });
613                }
614                _ => (),
615            }
616        }
617        Ok((
618            Expr::Path(ExprPath {
619                attrs: Vec::new(),
620                qself: type_path.qself,
621                path: type_path.path,
622            }),
623            helpful_error,
624        ))
625    } else {
626        p.parse().map(|x| (x, None))
627    }
628}
629
630/// Parse an XML name while resolving built-in namespace prefixes.
631fn parse_prefixed_name(
632    value: syn::parse::ParseStream<'_>,
633) -> Result<(Option<NamespaceRef>, NameRef)> {
634    if !value.peek(LitStr) {
635        // if we don't have a string literal next, we delegate to the default
636        // `NameRef` parser.
637        return Ok((None, value.parse()?));
638    }
639
640    let name: LitStr = value.parse()?;
641    let name_span = name.span();
642    let (prefix, name) = match name
643        .value()
644        .try_into()
645        .and_then(|name: rxml_validation::Name| name.split_name())
646    {
647        Ok(v) => v,
648        Err(e) => {
649            return Err(Error::new(
650                name_span,
651                format!("not a valid XML name: {}", e),
652            ))
653        }
654    };
655    let name = NameRef::Literal {
656        value: name,
657        span: name_span,
658    };
659    if let Some(prefix) = prefix {
660        let namespace_uri = match prefix.as_str() {
661            "xml" => XMLNS_XML,
662            "xmlns" => XMLNS_XMLNS,
663            other => return Err(Error::new(
664                name_span,
665                format!("prefix `{}` is not a built-in prefix and cannot be used. specify the desired namespace using the `namespace` key instead.", other)
666            )),
667        };
668        Ok((Some(NamespaceRef::fudge(namespace_uri, name_span)), name))
669    } else {
670        Ok((None, name))
671    }
672}
673
674/// Contents of an `#[xml(..)]` attribute on a struct or enum variant member.
675#[derive(Debug)]
676pub(crate) enum XmlFieldMeta {
677    /// `#[xml(attribute)]`, `#[xml(attribute = ..)]` or `#[xml(attribute(..))]`
678    Attribute {
679        /// The span of the `#[xml(attribute)]` meta from which this was parsed.
680        ///
681        /// This is useful for error messages.
682        span: Span,
683
684        /// The namespace/name keys.
685        qname: QNameRef,
686
687        /// The `default` flag.
688        default_: Flag,
689
690        /// An explicit type override, only usable within extracts.
691        type_: Option<Type>,
692
693        /// The path to the optional codec type.
694        codec: Option<Expr>,
695    },
696
697    /// `#[xml(text)]`
698    Text {
699        /// The span of the `#[xml(text)]` meta from which this was parsed.
700        ///
701        /// This is useful for error messages.
702        span: Span,
703
704        /// The path to the optional codec type.
705        codec: Option<Expr>,
706
707        /// An explicit type override, only usable within extracts.
708        type_: Option<Type>,
709    },
710
711    /// `#[xml(child)`
712    Child {
713        /// The span of the `#[xml(child)]` meta from which this was parsed.
714        ///
715        /// This is useful for error messages.
716        span: Span,
717
718        /// The `default` flag.
719        default_: Flag,
720
721        /// The `n` flag.
722        amount: Option<AmountConstraint>,
723    },
724
725    /// `#[xml(extract)]
726    Extract {
727        /// The span of the `#[xml(extract)]` meta from which this was parsed.
728        ///
729        /// This is useful for error messages.
730        span: Span,
731
732        /// The namespace/name keys.
733        qname: QNameRef,
734
735        /// The `n` flag.
736        amount: Option<AmountConstraint>,
737
738        /// The `default` flag.
739        default_: Flag,
740
741        /// The `fields` nested meta.
742        fields: Vec<XmlFieldMeta>,
743
744        /// The `on_unknown_attribute` value.
745        on_unknown_attribute: Option<Ident>,
746
747        /// The `on_unknown_child` value.
748        on_unknown_child: Option<Ident>,
749    },
750
751    /// `#[xml(element)]`
752    Element {
753        /// The span of the `#[xml(element)]` meta from which this was parsed.
754        ///
755        /// This is useful for error messages.
756        span: Span,
757
758        /// The `default` flag.
759        default_: Flag,
760
761        /// The `n` flag.
762        amount: Option<AmountConstraint>,
763    },
764
765    /// `#[xml(flag)]
766    Flag {
767        /// The span of the `#[xml(flag)]` meta from which this was parsed.
768        ///
769        /// This is useful for error messages.
770        span: Span,
771
772        /// The namespace/name keys.
773        qname: QNameRef,
774    },
775}
776
777impl XmlFieldMeta {
778    /// Parse a `#[xml(attribute(..))]` meta.
779    ///
780    /// That meta can have three distinct syntax styles:
781    /// - argument-less: `#[xml(attribute)]`
782    /// - shorthand: `#[xml(attribute = ..)]`
783    /// - full: `#[xml(attribute(..))]`
784    fn attribute_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
785        if meta.input.peek(Token![=]) {
786            // shorthand syntax
787            let (namespace, name) = parse_prefixed_name(meta.value()?)?;
788            Ok(Self::Attribute {
789                span: meta.path.span(),
790                qname: QNameRef {
791                    name: Some(name),
792                    namespace,
793                },
794                default_: Flag::Absent,
795                type_: None,
796                codec: None,
797            })
798        } else if meta.input.peek(syn::token::Paren) {
799            // full syntax
800            let mut qname = QNameRef::default();
801            let mut default_ = Flag::Absent;
802            let mut type_ = None;
803            let mut codec = None;
804            meta.parse_nested_meta(|meta| {
805                if meta.path.is_ident("default") {
806                    if default_.is_set() {
807                        return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
808                    }
809                    default_ = (&meta.path).into();
810                    Ok(())
811                } else if meta.path.is_ident("type_") {
812                    if type_.is_some() {
813                        return Err(Error::new_spanned(meta.path, "duplicate `type_` key"));
814                    }
815                    type_ = Some(meta.value()?.parse()?);
816                    Ok(())
817                } else if meta.path.is_ident("codec") {
818                    if codec.is_some() {
819                        return Err(Error::new_spanned(meta.path, "duplicate `codec` key"));
820                    }
821                    let (new_codec, helpful_error) = parse_codec_expr(meta.value()?)?;
822                    // See the comment at the top of text_from_meta() below for why we
823                    // do this.
824                    let lookahead = meta.input.lookahead1();
825                    if !lookahead.peek(Token![,]) && !meta.input.is_empty() {
826                        if let Some(helpful_error) = helpful_error {
827                            let mut e = lookahead.error();
828                            e.combine(helpful_error);
829                            return Err(e);
830                        }
831                    }
832                    codec = Some(new_codec);
833                    Ok(())
834                } else {
835                    match qname.parse_incremental_from_meta(meta)? {
836                        None => Ok(()),
837                        Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")),
838                    }
839                }
840            })?;
841            Ok(Self::Attribute {
842                span: meta.path.span(),
843                qname,
844                default_,
845                type_,
846                codec,
847            })
848        } else {
849            // argument-less syntax
850            Ok(Self::Attribute {
851                span: meta.path.span(),
852                qname: QNameRef::default(),
853                default_: Flag::Absent,
854                type_: None,
855                codec: None,
856            })
857        }
858    }
859
860    /// Parse a `#[xml(text)]` meta.
861    fn text_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
862        if meta.input.peek(Token![=]) {
863            let (codec, helpful_error) = parse_codec_expr(meta.value()?)?;
864            // A meta value can only be followed by either a `,`, or the end
865            // of the parse stream (because of the delimited group ending).
866            // Hence we check we are there. And if we are *not* there, we emit
867            // an error straight away, with the helpful addition from the
868            // `parse_codec_expr` if we have it.
869            //
870            // If we do not do this, the user gets a rather confusing
871            // "expected `,`" message if the `maybe_type_path` guess was
872            // wrong.
873            let lookahead = meta.input.lookahead1();
874            if !lookahead.peek(Token![,]) && !meta.input.is_empty() {
875                if let Some(helpful_error) = helpful_error {
876                    let mut e = lookahead.error();
877                    e.combine(helpful_error);
878                    return Err(e);
879                }
880            }
881            Ok(Self::Text {
882                span: meta.path.span(),
883                type_: None,
884                codec: Some(codec),
885            })
886        } else if meta.input.peek(syn::token::Paren) {
887            let mut codec: Option<Expr> = None;
888            let mut type_: Option<Type> = None;
889            meta.parse_nested_meta(|meta| {
890                if meta.path.is_ident("codec") {
891                    if codec.is_some() {
892                        return Err(Error::new_spanned(meta.path, "duplicate `codec` key"));
893                    }
894                    let (new_codec, helpful_error) = parse_codec_expr(meta.value()?)?;
895                    // See above (at the top-ish of this function) for why we
896                    // do this.
897                    let lookahead = meta.input.lookahead1();
898                    if !lookahead.peek(Token![,]) && !meta.input.is_empty() {
899                        if let Some(helpful_error) = helpful_error {
900                            let mut e = lookahead.error();
901                            e.combine(helpful_error);
902                            return Err(e);
903                        }
904                    }
905                    codec = Some(new_codec);
906                    Ok(())
907                } else if meta.path.is_ident("type_") {
908                    if type_.is_some() {
909                        return Err(Error::new_spanned(meta.path, "duplicate `type_` key"));
910                    }
911                    type_ = Some(meta.value()?.parse()?);
912                    Ok(())
913                } else {
914                    Err(Error::new_spanned(meta.path, "unsupported key"))
915                }
916            })?;
917            Ok(Self::Text {
918                span: meta.path.span(),
919                type_,
920                codec,
921            })
922        } else {
923            Ok(Self::Text {
924                span: meta.path.span(),
925                type_: None,
926                codec: None,
927            })
928        }
929    }
930
931    /// Parse a `#[xml(child)]` meta.
932    fn child_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
933        if meta.input.peek(syn::token::Paren) {
934            let mut default_ = Flag::Absent;
935            let mut amount = None;
936            meta.parse_nested_meta(|meta| {
937                if meta.path.is_ident("default") {
938                    if default_.is_set() {
939                        return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
940                    }
941                    default_ = (&meta.path).into();
942                    Ok(())
943                } else if meta.path.is_ident("n") {
944                    if amount.is_some() {
945                        return Err(Error::new_spanned(meta.path, "duplicate `n` key"));
946                    }
947                    amount = Some(meta.value()?.parse()?);
948                    Ok(())
949                } else {
950                    Err(Error::new_spanned(meta.path, "unsupported key"))
951                }
952            })?;
953            Ok(Self::Child {
954                span: meta.path.span(),
955                default_,
956                amount,
957            })
958        } else {
959            Ok(Self::Child {
960                span: meta.path.span(),
961                default_: Flag::Absent,
962                amount: None,
963            })
964        }
965    }
966
967    /// Parse a `#[xml(extract)]` meta.
968    fn extract_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
969        let mut qname = QNameRef::default();
970        let mut fields = None;
971        let mut amount = None;
972        let mut on_unknown_attribute = None;
973        let mut on_unknown_child = None;
974        let mut default_ = Flag::Absent;
975        meta.parse_nested_meta(|meta| {
976            if meta.path.is_ident("default") {
977                if default_.is_set() {
978                    return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
979                }
980                default_ = (&meta.path).into();
981                Ok(())
982            } else if meta.path.is_ident("fields") {
983                if let Some((fields_span, _)) = fields.as_ref() {
984                    let mut error = Error::new_spanned(meta.path, "duplicate `fields` meta");
985                    error.combine(Error::new(*fields_span, "previous `fields` meta was here"));
986                    return Err(error);
987                }
988                let mut new_fields = Vec::new();
989                meta.parse_nested_meta(|meta| {
990                    new_fields.push(XmlFieldMeta::parse_from_meta(meta)?);
991                    Ok(())
992                })?;
993                fields = Some((meta.path.span(), new_fields));
994                Ok(())
995            } else if meta.path.is_ident("n") {
996                if amount.is_some() {
997                    return Err(Error::new_spanned(meta.path, "duplicate `n` key"));
998                }
999                amount = Some(meta.value()?.parse()?);
1000                Ok(())
1001            } else if meta.path.is_ident("on_unknown_attribute") {
1002                if on_unknown_attribute.is_some() {
1003                    return Err(Error::new_spanned(
1004                        meta.path,
1005                        "duplicate `on_unknown_attribute` key",
1006                    ));
1007                }
1008                on_unknown_attribute = Some(meta.value()?.parse()?);
1009                Ok(())
1010            } else if meta.path.is_ident("on_unknown_child") {
1011                if on_unknown_child.is_some() {
1012                    return Err(Error::new_spanned(
1013                        meta.path,
1014                        "duplicate `on_unknown_child` key",
1015                    ));
1016                }
1017                on_unknown_child = Some(meta.value()?.parse()?);
1018                Ok(())
1019            } else {
1020                match qname.parse_incremental_from_meta(meta)? {
1021                    None => Ok(()),
1022                    Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")),
1023                }
1024            }
1025        })?;
1026        let fields = fields.map(|(_, x)| x).unwrap_or_else(Vec::new);
1027        Ok(Self::Extract {
1028            span: meta.path.span(),
1029            default_,
1030            qname,
1031            fields,
1032            amount,
1033            on_unknown_attribute,
1034            on_unknown_child,
1035        })
1036    }
1037
1038    /// Parse a `#[xml(element)]` meta.
1039    fn element_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
1040        let mut amount = None;
1041        let mut default_ = Flag::Absent;
1042        if meta.input.peek(syn::token::Paren) {
1043            meta.parse_nested_meta(|meta| {
1044                if meta.path.is_ident("default") {
1045                    if default_.is_set() {
1046                        return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
1047                    }
1048                    default_ = (&meta.path).into();
1049                    Ok(())
1050                } else if meta.path.is_ident("n") {
1051                    if amount.is_some() {
1052                        return Err(Error::new_spanned(meta.path, "duplicate `n` key"));
1053                    }
1054                    amount = Some(meta.value()?.parse()?);
1055                    Ok(())
1056                } else {
1057                    Err(Error::new_spanned(meta.path, "unsupported key"))
1058                }
1059            })?;
1060        }
1061        Ok(Self::Element {
1062            span: meta.path.span(),
1063            default_,
1064            amount,
1065        })
1066    }
1067
1068    /// Parse a `#[xml(flag)]` meta.
1069    fn flag_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
1070        let mut qname = QNameRef::default();
1071        if meta.input.peek(syn::token::Paren) {
1072            meta.parse_nested_meta(|meta| match qname.parse_incremental_from_meta(meta)? {
1073                None => Ok(()),
1074                Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")),
1075            })?;
1076        }
1077        Ok(Self::Flag {
1078            span: meta.path.span(),
1079            qname,
1080        })
1081    }
1082
1083    /// Parse [`Self`] from a nestd meta, switching on the identifier
1084    /// of that nested meta.
1085    fn parse_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
1086        if meta.path.is_ident("attribute") {
1087            Self::attribute_from_meta(meta)
1088        } else if meta.path.is_ident("text") {
1089            Self::text_from_meta(meta)
1090        } else if meta.path.is_ident("child") {
1091            Self::child_from_meta(meta)
1092        } else if meta.path.is_ident("extract") {
1093            Self::extract_from_meta(meta)
1094        } else if meta.path.is_ident("element") {
1095            Self::element_from_meta(meta)
1096        } else if meta.path.is_ident("flag") {
1097            Self::flag_from_meta(meta)
1098        } else {
1099            Err(Error::new_spanned(meta.path, "unsupported field meta"))
1100        }
1101    }
1102
1103    /// Parse an `#[xml(..)]` meta on a field.
1104    ///
1105    /// This switches based on the first identifier within the `#[xml(..)]`
1106    /// meta and generates an enum variant accordingly.
1107    ///
1108    /// Only a single nested meta is allowed; more than one will be
1109    /// rejected with an appropriate compile-time error.
1110    ///
1111    /// If no meta is contained at all, a compile-time error is generated.
1112    ///
1113    /// Undefined options or options with incompatible values are rejected
1114    /// with an appropriate compile-time error.
1115    pub(crate) fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
1116        let mut result: Option<Self> = None;
1117
1118        attr.parse_nested_meta(|meta| {
1119            if result.is_some() {
1120                return Err(Error::new_spanned(
1121                    meta.path,
1122                    "multiple field type specifiers are not supported",
1123                ));
1124            }
1125
1126            result = Some(Self::parse_from_meta(meta)?);
1127            Ok(())
1128        })?;
1129
1130        if let Some(result) = result {
1131            Ok(result)
1132        } else {
1133            Err(Error::new_spanned(
1134                attr,
1135                "missing field type specifier within `#[xml(..)]`",
1136            ))
1137        }
1138    }
1139
1140    /// Find and parse a `#[xml(..)]` meta on a field.
1141    ///
1142    /// This invokes [`Self::parse_from_attribute`] internally on the first
1143    /// encountered `#[xml(..)]` meta.
1144    ///
1145    /// If not exactly one `#[xml(..)]` meta is encountered, an error is
1146    /// returned. The error is spanned to `err_span`.
1147    pub(crate) fn parse_from_attributes(attrs: &[Attribute], err_span: &Span) -> Result<Self> {
1148        let mut result: Option<Self> = None;
1149        for attr in attrs {
1150            if !attr.path().is_ident("xml") {
1151                continue;
1152            }
1153
1154            if result.is_some() {
1155                return Err(Error::new_spanned(
1156                    attr,
1157                    "only one #[xml(..)] attribute per field allowed.",
1158                ));
1159            }
1160
1161            result = Some(Self::parse_from_attribute(attr)?);
1162        }
1163
1164        if let Some(result) = result {
1165            Ok(result)
1166        } else {
1167            Err(Error::new(*err_span, "missing #[xml(..)] meta on field"))
1168        }
1169    }
1170
1171    /// Return a span which points at the meta which constructed this
1172    /// XmlFieldMeta.
1173    pub(crate) fn span(&self) -> Span {
1174        match self {
1175            Self::Attribute { ref span, .. } => *span,
1176            Self::Child { ref span, .. } => *span,
1177            Self::Text { ref span, .. } => *span,
1178            Self::Extract { ref span, .. } => *span,
1179            Self::Element { ref span, .. } => *span,
1180            Self::Flag { ref span, .. } => *span,
1181        }
1182    }
1183
1184    /// Extract an explicit type specification if it exists.
1185    pub(crate) fn take_type(&mut self) -> Option<Type> {
1186        match self {
1187            Self::Attribute { ref mut type_, .. } => type_.take(),
1188            Self::Text { ref mut type_, .. } => type_.take(),
1189            _ => None,
1190        }
1191    }
1192}