structs.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//! Handling of structs
  8
  9use proc_macro2::TokenStream;
 10use quote::quote;
 11use syn::*;
 12
 13use crate::compound::Compound;
 14use crate::meta::{NameRef, NamespaceRef, XmlCompoundMeta};
 15
 16/// Parts necessary to construct a `::xso::FromXml` implementation.
 17pub(crate) struct FromXmlParts {
 18    /// Additional items necessary for the implementation.
 19    pub(crate) defs: TokenStream,
 20
 21    /// The body of the `::xso::FromXml::from_xml` function.
 22    pub(crate) from_events_body: TokenStream,
 23
 24    /// The name of the type which is the `::xso::FromXml::Builder`.
 25    pub(crate) builder_ty_ident: Ident,
 26}
 27
 28/// Parts necessary to construct a `::xso::IntoXml` implementation.
 29pub(crate) struct IntoXmlParts {
 30    /// Additional items necessary for the implementation.
 31    pub(crate) defs: TokenStream,
 32
 33    /// The body of the `::xso::IntoXml::into_event_iter` function.
 34    pub(crate) into_event_iter_body: TokenStream,
 35
 36    /// The name of the type which is the `::xso::IntoXml::EventIter`.
 37    pub(crate) event_iter_ty_ident: Ident,
 38}
 39
 40/// Definition of a struct and how to parse it.
 41pub(crate) struct StructDef {
 42    /// The XML namespace of the element to map the struct to.
 43    namespace: NamespaceRef,
 44
 45    /// The XML name of the element to map the struct to.
 46    name: NameRef,
 47
 48    /// The field(s) of this struct.
 49    inner: Compound,
 50
 51    /// Name of the target type.
 52    target_ty_ident: Ident,
 53
 54    /// Name of the builder type.
 55    builder_ty_ident: Ident,
 56
 57    /// Name of the iterator type.
 58    event_iter_ty_ident: Ident,
 59}
 60
 61impl StructDef {
 62    /// Create a new struct from its name, meta, and fields.
 63    pub(crate) fn new(ident: &Ident, meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> {
 64        let Some(namespace) = meta.namespace else {
 65            return Err(Error::new(meta.span, "`namespace` is required on structs"));
 66        };
 67
 68        let Some(name) = meta.name else {
 69            return Err(Error::new(meta.span, "`name` is required on structs"));
 70        };
 71
 72        Ok(Self {
 73            namespace,
 74            name,
 75            inner: Compound::from_fields(fields)?,
 76            target_ty_ident: ident.clone(),
 77            builder_ty_ident: quote::format_ident!("{}FromXmlBuilder", ident),
 78            event_iter_ty_ident: quote::format_ident!("{}IntoXmlIterator", ident),
 79        })
 80    }
 81
 82    pub(crate) fn make_from_events_builder(
 83        &self,
 84        vis: &Visibility,
 85        name_ident: &Ident,
 86        attrs_ident: &Ident,
 87    ) -> Result<FromXmlParts> {
 88        let xml_namespace = &self.namespace;
 89        let xml_name = &self.name;
 90
 91        let target_ty_ident = &self.target_ty_ident;
 92        let builder_ty_ident = &self.builder_ty_ident;
 93        let state_ty_ident = quote::format_ident!("{}State", builder_ty_ident);
 94
 95        let defs = self
 96            .inner
 97            .make_from_events_statemachine(
 98                &state_ty_ident,
 99                &Path::from(target_ty_ident.clone()).into(),
100                "Struct",
101            )?
102            .with_augmented_init(|init| {
103                quote! {
104                    if name.0 != #xml_namespace || name.1 != #xml_name {
105                        ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch {
106                            name,
107                            attrs,
108                        })
109                    } else {
110                        #init
111                    }
112                }
113            })
114            .compile()
115            .render(
116                vis,
117                &builder_ty_ident,
118                &state_ty_ident,
119                &TypePath {
120                    qself: None,
121                    path: target_ty_ident.clone().into(),
122                }
123                .into(),
124            )?;
125
126        Ok(FromXmlParts {
127            defs,
128            from_events_body: quote! {
129                #builder_ty_ident::new(#name_ident, #attrs_ident)
130            },
131            builder_ty_ident: builder_ty_ident.clone(),
132        })
133    }
134
135    pub(crate) fn make_into_event_iter(&self, vis: &Visibility) -> Result<IntoXmlParts> {
136        let xml_namespace = &self.namespace;
137        let xml_name = &self.name;
138
139        let target_ty_ident = &self.target_ty_ident;
140        let event_iter_ty_ident = &self.event_iter_ty_ident;
141        let state_ty_ident = quote::format_ident!("{}State", event_iter_ty_ident);
142
143        let defs = self
144            .inner
145            .make_into_event_iter_statemachine(&target_ty_ident.clone().into(), "Struct")?
146            .with_augmented_init(|init| {
147                quote! {
148                    let name = (
149                        ::xso::exports::rxml::Namespace::from(#xml_namespace),
150                        #xml_name.into(),
151                    );
152                    #init
153                }
154            })
155            .compile()
156            .render(
157                vis,
158                &TypePath {
159                    qself: None,
160                    path: target_ty_ident.clone().into(),
161                }
162                .into(),
163                &state_ty_ident,
164                &event_iter_ty_ident,
165            )?;
166
167        Ok(IntoXmlParts {
168            defs,
169            into_event_iter_body: quote! {
170                #event_iter_ty_ident::new(self)
171            },
172            event_iter_ty_ident: event_iter_ty_ident.clone(),
173        })
174    }
175}