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    /// Flag whether debug mode is enabled.
 61    debug: bool,
 62}
 63
 64impl StructDef {
 65    /// Create a new struct from its name, meta, and fields.
 66    pub(crate) fn new(ident: &Ident, meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> {
 67        let Some(namespace) = meta.namespace else {
 68            return Err(Error::new(meta.span, "`namespace` is required on structs"));
 69        };
 70
 71        let Some(name) = meta.name else {
 72            return Err(Error::new(meta.span, "`name` is required on structs"));
 73        };
 74
 75        Ok(Self {
 76            namespace,
 77            name,
 78            inner: Compound::from_fields(fields)?,
 79            target_ty_ident: ident.clone(),
 80            builder_ty_ident: quote::format_ident!("{}FromXmlBuilder", ident),
 81            event_iter_ty_ident: quote::format_ident!("{}IntoXmlIterator", ident),
 82            debug: meta.debug.is_set(),
 83        })
 84    }
 85
 86    pub(crate) fn make_from_events_builder(
 87        &self,
 88        vis: &Visibility,
 89        name_ident: &Ident,
 90        attrs_ident: &Ident,
 91    ) -> Result<FromXmlParts> {
 92        let xml_namespace = &self.namespace;
 93        let xml_name = &self.name;
 94
 95        let target_ty_ident = &self.target_ty_ident;
 96        let builder_ty_ident = &self.builder_ty_ident;
 97        let state_ty_ident = quote::format_ident!("{}State", builder_ty_ident);
 98
 99        let defs = self
100            .inner
101            .make_from_events_statemachine(
102                &state_ty_ident,
103                &Path::from(target_ty_ident.clone()).into(),
104                "Struct",
105            )?
106            .with_augmented_init(|init| {
107                quote! {
108                    if name.0 != #xml_namespace || name.1 != #xml_name {
109                        ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch {
110                            name,
111                            attrs,
112                        })
113                    } else {
114                        #init
115                    }
116                }
117            })
118            .compile()
119            .render(
120                vis,
121                builder_ty_ident,
122                &state_ty_ident,
123                &TypePath {
124                    qself: None,
125                    path: target_ty_ident.clone().into(),
126                }
127                .into(),
128            )?;
129
130        Ok(FromXmlParts {
131            defs,
132            from_events_body: quote! {
133                #builder_ty_ident::new(#name_ident, #attrs_ident)
134            },
135            builder_ty_ident: builder_ty_ident.clone(),
136        })
137    }
138
139    pub(crate) fn make_into_event_iter(&self, vis: &Visibility) -> Result<IntoXmlParts> {
140        let xml_namespace = &self.namespace;
141        let xml_name = &self.name;
142
143        let target_ty_ident = &self.target_ty_ident;
144        let event_iter_ty_ident = &self.event_iter_ty_ident;
145        let state_ty_ident = quote::format_ident!("{}State", event_iter_ty_ident);
146
147        let defs = self
148            .inner
149            .make_into_event_iter_statemachine(&target_ty_ident.clone().into(), "Struct")?
150            .with_augmented_init(|init| {
151                quote! {
152                    let name = (
153                        ::xso::exports::rxml::Namespace::from(#xml_namespace),
154                        #xml_name.into(),
155                    );
156                    #init
157                }
158            })
159            .compile()
160            .render(
161                vis,
162                &TypePath {
163                    qself: None,
164                    path: target_ty_ident.clone().into(),
165                }
166                .into(),
167                &state_ty_ident,
168                event_iter_ty_ident,
169            )?;
170
171        Ok(IntoXmlParts {
172            defs,
173            into_event_iter_body: quote! {
174                #event_iter_ty_ident::new(self)
175            },
176            event_iter_ty_ident: event_iter_ty_ident.clone(),
177        })
178    }
179
180    pub(crate) fn debug(&self) -> bool {
181        self.debug
182    }
183}