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}