meta.rs

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