attribute.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//! 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}