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 proc_macro2::{Span, TokenStream};
13use quote::{quote, quote_spanned};
14use syn::{meta::ParseNestedMeta, spanned::Spanned, *};
15
16use rxml_validation::NcName;
17
18/// XML core namespace URI (for the `xml:` prefix)
19pub const XMLNS_XML: &str = "http://www.w3.org/XML/1998/namespace";
20/// XML namespace URI (for the `xmlns:` prefix)
21pub const XMLNS_XMLNS: &str = "http://www.w3.org/2000/xmlns/";
22
23/// Value for the `#[xml(namespace = ..)]` attribute.
24#[derive(Debug)]
25pub(crate) enum NamespaceRef {
26 /// The XML namespace is specified as a string literal.
27 LitStr(LitStr),
28
29 /// The XML namespace is specified as a path.
30 Path(Path),
31}
32
33impl NamespaceRef {
34 fn fudge(value: &str, span: Span) -> Self {
35 Self::LitStr(LitStr::new(value, span))
36 }
37}
38
39impl syn::parse::Parse for NamespaceRef {
40 fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
41 if input.peek(syn::LitStr) {
42 Ok(Self::LitStr(input.parse()?))
43 } else {
44 Ok(Self::Path(input.parse()?))
45 }
46 }
47}
48
49impl quote::ToTokens for NamespaceRef {
50 fn to_tokens(&self, tokens: &mut TokenStream) {
51 match self {
52 Self::LitStr(ref lit) => lit.to_tokens(tokens),
53 Self::Path(ref path) => path.to_tokens(tokens),
54 }
55 }
56}
57
58/// Value for the `#[xml(name = .. )]` attribute.
59#[derive(Debug)]
60pub(crate) enum NameRef {
61 /// The XML name is specified as a string literal.
62 Literal {
63 /// The validated XML name.
64 value: NcName,
65
66 /// The span of the original [`syn::LitStr`].
67 span: Span,
68 },
69
70 /// The XML name is specified as a path.
71 Path(Path),
72}
73
74impl syn::parse::Parse for NameRef {
75 fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
76 if input.peek(syn::LitStr) {
77 let s: LitStr = input.parse()?;
78 let span = s.span();
79 match NcName::try_from(s.value()) {
80 Ok(value) => Ok(Self::Literal { value, span }),
81 Err(e) => Err(Error::new(span, format!("not a valid XML name: {}", e))),
82 }
83 } else {
84 let p: Path = input.parse()?;
85 Ok(Self::Path(p))
86 }
87 }
88}
89
90impl quote::ToTokens for NameRef {
91 fn to_tokens(&self, tokens: &mut TokenStream) {
92 match self {
93 Self::Literal { ref value, span } => {
94 let span = *span;
95 let value = value.as_str();
96 let value = quote_spanned! { span=> #value };
97 // SAFETY: self.0 is a known-good NcName, so converting it to an
98 // NcNameStr is known to be safe.
99 // NOTE: we cannot use `quote_spanned! { self.span=> }` for the unsafe
100 // block as that would then in fact trip a `#[deny(unsafe_code)]` lint
101 // at the use site of the macro.
102 tokens.extend(quote! {
103 unsafe { ::xso::exports::rxml::NcNameStr::from_str_unchecked(#value) }
104 })
105 }
106 Self::Path(ref path) => path.to_tokens(tokens),
107 }
108 }
109}
110
111/// Represents a boolean flag from a `#[xml(..)]` attribute meta.
112#[derive(Clone, Copy, Debug)]
113pub(crate) enum Flag {
114 /// The flag is not set.
115 Absent,
116
117 /// The flag was set.
118 Present(
119 /// The span of the syntax element which enabled the flag.
120 ///
121 /// This is used to generate useful error messages by pointing at the
122 /// specific place the flag was activated.
123 #[allow(dead_code)]
124 Span,
125 ),
126}
127
128impl Flag {
129 /// Return true if the flag is set, false otherwise.
130 pub(crate) fn is_set(&self) -> bool {
131 match self {
132 Self::Absent => false,
133 Self::Present(_) => true,
134 }
135 }
136}
137
138impl<T: Spanned> From<T> for Flag {
139 fn from(other: T) -> Flag {
140 Flag::Present(other.span())
141 }
142}
143
144/// Contents of an `#[xml(..)]` attribute on a struct, enum variant, or enum.
145#[derive(Debug)]
146pub(crate) struct XmlCompoundMeta {
147 /// The span of the `#[xml(..)]` meta from which this was parsed.
148 ///
149 /// This is useful for error messages.
150 pub(crate) span: Span,
151
152 /// The value assigned to `namespace` inside `#[xml(..)]`, if any.
153 pub(crate) namespace: Option<NamespaceRef>,
154
155 /// The value assigned to `name` inside `#[xml(..)]`, if any.
156 pub(crate) name: Option<NameRef>,
157
158 /// The debug flag.
159 pub(crate) debug: Flag,
160
161 /// The value assigned to `builder` inside `#[xml(..)]`, if any.
162 pub(crate) builder: Option<Ident>,
163
164 /// The value assigned to `iterator` inside `#[xml(..)]`, if any.
165 pub(crate) iterator: Option<Ident>,
166}
167
168impl XmlCompoundMeta {
169 /// Parse the meta values from a `#[xml(..)]` attribute.
170 ///
171 /// Undefined options or options with incompatible values are rejected
172 /// with an appropriate compile-time error.
173 fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
174 let mut namespace = None;
175 let mut name = None;
176 let mut builder = None;
177 let mut iterator = None;
178 let mut debug = Flag::Absent;
179
180 attr.parse_nested_meta(|meta| {
181 if meta.path.is_ident("name") {
182 if name.is_some() {
183 return Err(Error::new_spanned(meta.path, "duplicate `name` key"));
184 }
185 name = Some(meta.value()?.parse()?);
186 Ok(())
187 } else if meta.path.is_ident("namespace") {
188 if namespace.is_some() {
189 return Err(Error::new_spanned(meta.path, "duplicate `namespace` key"));
190 }
191 namespace = Some(meta.value()?.parse()?);
192 Ok(())
193 } else if meta.path.is_ident("debug") {
194 if debug.is_set() {
195 return Err(Error::new_spanned(meta.path, "duplicate `debug` key"));
196 }
197 debug = (&meta.path).into();
198 Ok(())
199 } else if meta.path.is_ident("builder") {
200 if builder.is_some() {
201 return Err(Error::new_spanned(meta.path, "duplicate `builder` key"));
202 }
203 builder = Some(meta.value()?.parse()?);
204 Ok(())
205 } else if meta.path.is_ident("iterator") {
206 if iterator.is_some() {
207 return Err(Error::new_spanned(meta.path, "duplicate `iterator` key"));
208 }
209 iterator = Some(meta.value()?.parse()?);
210 Ok(())
211 } else {
212 Err(Error::new_spanned(meta.path, "unsupported key"))
213 }
214 })?;
215
216 Ok(Self {
217 span: attr.span(),
218 namespace,
219 name,
220 debug,
221 builder,
222 iterator,
223 })
224 }
225
226 /// Search through `attrs` for a single `#[xml(..)]` attribute and parse
227 /// it.
228 ///
229 /// Undefined options or options with incompatible values are rejected
230 /// with an appropriate compile-time error.
231 ///
232 /// If more than one `#[xml(..)]` attribute is found, an error is
233 /// emitted.
234 ///
235 /// If no `#[xml(..)]` attribute is found, `None` is returned.
236 pub(crate) fn try_parse_from_attributes(attrs: &[Attribute]) -> Result<Option<Self>> {
237 let mut result = None;
238 for attr in attrs {
239 if !attr.path().is_ident("xml") {
240 continue;
241 }
242 if result.is_some() {
243 return Err(syn::Error::new_spanned(
244 attr.path(),
245 "only one #[xml(..)] per struct or enum variant allowed",
246 ));
247 }
248 result = Some(Self::parse_from_attribute(attr)?);
249 }
250 Ok(result)
251 }
252
253 /// Search through `attrs` for a single `#[xml(..)]` attribute and parse
254 /// it.
255 ///
256 /// Undefined options or options with incompatible values are rejected
257 /// with an appropriate compile-time error.
258 ///
259 /// If more than one or no `#[xml(..)]` attribute is found, an error is
260 /// emitted.
261 pub(crate) fn parse_from_attributes(attrs: &[Attribute]) -> Result<Self> {
262 match Self::try_parse_from_attributes(attrs)? {
263 Some(v) => Ok(v),
264 None => Err(syn::Error::new(
265 Span::call_site(),
266 "#[xml(..)] attribute required on struct or enum variant",
267 )),
268 }
269 }
270}
271
272/// Parse an XML name while resolving built-in namespace prefixes.
273fn parse_prefixed_name(
274 value: syn::parse::ParseStream<'_>,
275) -> Result<(Option<NamespaceRef>, NameRef)> {
276 if !value.peek(LitStr) {
277 // if we don't have a string literal next, we delegate to the default
278 // `NameRef` parser.
279 return Ok((None, value.parse()?));
280 }
281
282 let name: LitStr = value.parse()?;
283 let name_span = name.span();
284 let (prefix, name) = match name
285 .value()
286 .try_into()
287 .and_then(|name: rxml_validation::Name| name.split_name())
288 {
289 Ok(v) => v,
290 Err(e) => {
291 return Err(Error::new(
292 name_span,
293 format!("not a valid XML name: {}", e),
294 ))
295 }
296 };
297 let name = NameRef::Literal {
298 value: name,
299 span: name_span,
300 };
301 if let Some(prefix) = prefix {
302 let namespace_uri = match prefix.as_str() {
303 "xml" => XMLNS_XML,
304 "xmlns" => XMLNS_XMLNS,
305 other => return Err(Error::new(
306 name_span,
307 format!("prefix `{}` is not a built-in prefix and cannot be used. specify the desired namespace using the `namespace` key instead.", other)
308 )),
309 };
310 Ok((Some(NamespaceRef::fudge(namespace_uri, name_span)), name))
311 } else {
312 Ok((None, name))
313 }
314}
315
316/// Contents of an `#[xml(..)]` attribute on a struct or enum variant member.
317#[derive(Debug)]
318pub(crate) enum XmlFieldMeta {
319 /// `#[xml(attribute)]`, `#[xml(attribute = ..)]` or `#[xml(attribute(..))]`
320 Attribute {
321 /// The span of the `#[xml(attribute)]` meta from which this was parsed.
322 ///
323 /// This is useful for error messages.
324 span: Span,
325
326 /// The XML namespace supplied.
327 namespace: Option<NamespaceRef>,
328
329 /// The XML name supplied.
330 name: Option<NameRef>,
331
332 /// The `default` flag.
333 default_: Flag,
334 },
335
336 /// `#[xml(text)]`
337 Text {
338 /// The path to the optional codec type.
339 codec: Option<Type>,
340 },
341
342 /// `#[xml(child)`
343 Child {
344 /// The `default` flag.
345 default_: Flag,
346 },
347}
348
349impl XmlFieldMeta {
350 /// Parse a `#[xml(attribute(..))]` meta.
351 ///
352 /// That meta can have three distinct syntax styles:
353 /// - argument-less: `#[xml(attribute)]`
354 /// - shorthand: `#[xml(attribute = ..)]`
355 /// - full: `#[xml(attribute(..))]`
356 fn attribute_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
357 if meta.input.peek(Token![=]) {
358 // shorthand syntax
359 let (namespace, name) = parse_prefixed_name(meta.value()?)?;
360 Ok(Self::Attribute {
361 span: meta.path.span(),
362 name: Some(name),
363 namespace,
364 default_: Flag::Absent,
365 })
366 } else if meta.input.peek(syn::token::Paren) {
367 // full syntax
368 let mut name: Option<NameRef> = None;
369 let mut namespace: Option<NamespaceRef> = None;
370 let mut default_ = Flag::Absent;
371 meta.parse_nested_meta(|meta| {
372 if meta.path.is_ident("name") {
373 if name.is_some() {
374 return Err(Error::new_spanned(meta.path, "duplicate `name` key"));
375 }
376 let value = meta.value()?;
377 let name_span = value.span();
378 let (new_namespace, new_name) = parse_prefixed_name(value)?;
379 if let Some(new_namespace) = new_namespace {
380 if namespace.is_some() {
381 return Err(Error::new(
382 name_span,
383 "cannot combine `namespace` key with prefixed `name`",
384 ));
385 }
386 namespace = Some(new_namespace);
387 }
388 name = Some(new_name);
389 Ok(())
390 } else if meta.path.is_ident("namespace") {
391 if namespace.is_some() {
392 return Err(Error::new_spanned(
393 meta.path,
394 "duplicate `namespace` key or `name` key has prefix",
395 ));
396 }
397 namespace = Some(meta.value()?.parse()?);
398 Ok(())
399 } else if meta.path.is_ident("default") {
400 if default_.is_set() {
401 return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
402 }
403 default_ = (&meta.path).into();
404 Ok(())
405 } else {
406 Err(Error::new_spanned(meta.path, "unsupported key"))
407 }
408 })?;
409 Ok(Self::Attribute {
410 span: meta.path.span(),
411 name,
412 namespace,
413 default_,
414 })
415 } else {
416 // argument-less syntax
417 Ok(Self::Attribute {
418 span: meta.path.span(),
419 name: None,
420 namespace: None,
421 default_: Flag::Absent,
422 })
423 }
424 }
425
426 /// Parse a `#[xml(text)]` meta.
427 fn text_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
428 let mut codec: Option<Type> = None;
429 if meta.input.peek(Token![=]) {
430 Ok(Self::Text {
431 codec: Some(meta.value()?.parse()?),
432 })
433 } else if meta.input.peek(syn::token::Paren) {
434 meta.parse_nested_meta(|meta| {
435 if meta.path.is_ident("codec") {
436 if codec.is_some() {
437 return Err(Error::new_spanned(meta.path, "duplicate `codec` key"));
438 }
439 codec = Some(meta.value()?.parse()?);
440 Ok(())
441 } else {
442 Err(Error::new_spanned(meta.path, "unsupported key"))
443 }
444 })?;
445 Ok(Self::Text { codec })
446 } else {
447 Ok(Self::Text { codec: None })
448 }
449 }
450
451 /// Parse a `#[xml(child)]` meta.
452 fn child_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
453 if meta.input.peek(syn::token::Paren) {
454 let mut default_ = Flag::Absent;
455 meta.parse_nested_meta(|meta| {
456 if meta.path.is_ident("default") {
457 if default_.is_set() {
458 return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
459 }
460 default_ = (&meta.path).into();
461 Ok(())
462 } else {
463 Err(Error::new_spanned(meta.path, "unsupported key"))
464 }
465 })?;
466 Ok(Self::Child { default_ })
467 } else {
468 Ok(Self::Child {
469 default_: Flag::Absent,
470 })
471 }
472 }
473
474 /// Parse [`Self`] from a nestd meta, switching on the identifier
475 /// of that nested meta.
476 fn parse_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
477 if meta.path.is_ident("attribute") {
478 Self::attribute_from_meta(meta)
479 } else if meta.path.is_ident("text") {
480 Self::text_from_meta(meta)
481 } else if meta.path.is_ident("child") {
482 Self::child_from_meta(meta)
483 } else {
484 Err(Error::new_spanned(meta.path, "unsupported field meta"))
485 }
486 }
487
488 /// Parse an `#[xml(..)]` meta on a field.
489 ///
490 /// This switches based on the first identifier within the `#[xml(..)]`
491 /// meta and generates an enum variant accordingly.
492 ///
493 /// Only a single nested meta is allowed; more than one will be
494 /// rejected with an appropriate compile-time error.
495 ///
496 /// If no meta is contained at all, a compile-time error is generated.
497 ///
498 /// Undefined options or options with incompatible values are rejected
499 /// with an appropriate compile-time error.
500 pub(crate) fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
501 let mut result: Option<Self> = None;
502
503 attr.parse_nested_meta(|meta| {
504 if result.is_some() {
505 return Err(Error::new_spanned(
506 meta.path,
507 "multiple field type specifiers are not supported",
508 ));
509 }
510
511 result = Some(Self::parse_from_meta(meta)?);
512 Ok(())
513 })?;
514
515 if let Some(result) = result {
516 Ok(result)
517 } else {
518 Err(Error::new_spanned(
519 attr,
520 "missing field type specifier within `#[xml(..)]`",
521 ))
522 }
523 }
524
525 /// Find and parse a `#[xml(..)]` meta on a field.
526 ///
527 /// This invokes [`Self::parse_from_attribute`] internally on the first
528 /// encountered `#[xml(..)]` meta.
529 ///
530 /// If not exactly one `#[xml(..)]` meta is encountered, an error is
531 /// returned. The error is spanned to `err_span`.
532 pub(crate) fn parse_from_attributes(attrs: &[Attribute], err_span: &Span) -> Result<Self> {
533 let mut result: Option<Self> = None;
534 for attr in attrs {
535 if !attr.path().is_ident("xml") {
536 continue;
537 }
538
539 if result.is_some() {
540 return Err(Error::new_spanned(
541 attr,
542 "only one #[xml(..)] attribute per field allowed.",
543 ));
544 }
545
546 result = Some(Self::parse_from_attribute(attr)?);
547 }
548
549 if let Some(result) = result {
550 Ok(result)
551 } else {
552 Err(Error::new(*err_span, "missing #[xml(..)] meta on field"))
553 }
554 }
555}