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