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