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