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//! This module concerns the processing of attributes.
8//!
9//! In particular, it provides the `#[xml(attribute)]` implementation.
10
11use quote::{quote, ToTokens};
12use syn::*;
13
14use crate::error_message::{self, ParentRef};
15use crate::meta::{Flag, NameRef, NamespaceRef};
16use crate::scope::{AsItemsScope, FromEventsScope};
17use crate::types::{as_optional_xml_text_fn, default_fn, from_xml_text_fn};
18
19use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit};
20
21/// The field maps to an attribute.
22pub(super) struct AttributeField {
23 /// The optional XML namespace of the attribute.
24 pub(super) xml_namespace: Option<NamespaceRef>,
25
26 /// The XML name of the attribute.
27 pub(super) xml_name: NameRef,
28
29 /// Flag indicating whether the value should be defaulted if the
30 /// attribute is absent.
31 pub(super) default_: Flag,
32}
33
34impl Field for AttributeField {
35 fn make_builder_part(
36 &self,
37 scope: &FromEventsScope,
38 container_name: &ParentRef,
39 member: &Member,
40 ty: &Type,
41 ) -> Result<FieldBuilderPart> {
42 let FromEventsScope { ref attrs, .. } = scope;
43 let ty = ty.clone();
44 let xml_namespace = &self.xml_namespace;
45 let xml_name = &self.xml_name;
46
47 let missing_msg = error_message::on_missing_attribute(container_name, member);
48
49 let xml_namespace = match xml_namespace {
50 Some(v) => v.to_token_stream(),
51 None => quote! {
52 ::xso::exports::rxml::Namespace::none()
53 },
54 };
55
56 let from_xml_text = from_xml_text_fn(ty.clone());
57
58 let on_absent = match self.default_ {
59 Flag::Absent => quote! {
60 return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
61 },
62 Flag::Present(_) => {
63 let default_ = default_fn(ty.clone());
64 quote! {
65 #default_()
66 }
67 }
68 };
69
70 Ok(FieldBuilderPart::Init {
71 value: FieldTempInit {
72 init: quote! {
73 match #attrs.remove(#xml_namespace, #xml_name).map(#from_xml_text).transpose()? {
74 ::core::option::Option::Some(v) => v,
75 ::core::option::Option::None => #on_absent,
76 }
77 },
78 ty: ty.clone(),
79 },
80 })
81 }
82
83 fn make_iterator_part(
84 &self,
85 _scope: &AsItemsScope,
86 _container_name: &ParentRef,
87 bound_name: &Ident,
88 _member: &Member,
89 ty: &Type,
90 ) -> Result<FieldIteratorPart> {
91 let xml_namespace = match self.xml_namespace {
92 Some(ref v) => quote! { ::xso::exports::rxml::Namespace::from(#v) },
93 None => quote! {
94 ::xso::exports::rxml::Namespace::NONE
95 },
96 };
97 let xml_name = &self.xml_name;
98
99 let as_optional_xml_text = as_optional_xml_text_fn(ty.clone());
100
101 Ok(FieldIteratorPart::Header {
102 generator: quote! {
103 #as_optional_xml_text(#bound_name)?.map(|#bound_name| ::xso::Item::Attribute(
104 #xml_namespace,
105 ::std::borrow::Cow::Borrowed(#xml_name),
106 #bound_name,
107 ));
108 },
109 })
110 }
111}