1use proc_macro2::{Span, TokenStream};
10use quote::{quote, ToTokens};
11use syn::{spanned::Spanned, *};
12
13use crate::common::{AsXmlParts, FromXmlParts, GenericsInfo, ItemDef, XmlNameMatcher};
14use crate::compound::Compound;
15use crate::error_message::ParentRef;
16use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta};
17use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State};
18use crate::types::{
19 as_xml_iter_fn, feed_fn, from_events_fn, from_xml_builder_ty, item_iter_ty, ref_ty,
20};
21
22pub(crate) enum StructInner {
26 Transparent {
37 member: Member,
39
40 ty: Type,
42 },
43
44 Compound {
48 xml_namespace: NamespaceRef,
50
51 xml_name: NameRef,
53
54 inner: Compound,
56 },
57}
58
59impl StructInner {
60 pub(crate) fn new(meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> {
61 let XmlCompoundMeta {
65 span: meta_span,
66 qname: QNameRef { namespace, name },
67 exhaustive,
68 debug,
69 builder,
70 iterator,
71 on_unknown_attribute,
72 on_unknown_child,
73 transparent,
74 discard,
75 deserialize_callback,
76 attribute,
77 value,
78 } = meta;
79
80 assert!(builder.is_none());
84 assert!(iterator.is_none());
85 assert!(!debug.is_set());
86 assert!(deserialize_callback.is_none());
87
88 reject_key!(exhaustive flag not on "structs" only on "enums");
89 reject_key!(attribute not on "structs" only on "enums");
90 reject_key!(value not on "structs" only on "attribute-switched enum variants");
91
92 if let Flag::Present(_) = transparent {
93 reject_key!(namespace not on "transparent structs");
94 reject_key!(name not on "transparent structs");
95 reject_key!(on_unknown_attribute not on "transparent structs");
96 reject_key!(on_unknown_child not on "transparent structs");
97 reject_key!(discard vec not on "transparent structs");
98
99 let fields_span = fields.span();
100 let fields = match fields {
101 Fields::Unit => {
102 return Err(Error::new(
103 fields_span,
104 "transparent structs or enum variants must have exactly one field",
105 ))
106 }
107 Fields::Named(FieldsNamed {
108 named: ref fields, ..
109 })
110 | Fields::Unnamed(FieldsUnnamed {
111 unnamed: ref fields,
112 ..
113 }) => fields,
114 };
115
116 if fields.len() != 1 {
117 return Err(Error::new(
118 fields_span,
119 "transparent structs or enum variants must have exactly one field",
120 ));
121 }
122
123 let field = &fields[0];
124 for attr in field.attrs.iter() {
125 if attr.meta.path().is_ident("xml") {
126 return Err(Error::new_spanned(
127 attr,
128 "#[xml(..)] attributes are not allowed inside transparent structs",
129 ));
130 }
131 }
132 let member = match field.ident.as_ref() {
133 Some(v) => Member::Named(v.clone()),
134 None => Member::Unnamed(Index {
135 span: field.ty.span(),
136 index: 0,
137 }),
138 };
139 let ty = field.ty.clone();
140 Ok(Self::Transparent { ty, member })
141 } else {
142 let Some(xml_namespace) = namespace else {
143 return Err(Error::new(
144 meta_span,
145 "`namespace` is required on non-transparent structs",
146 ));
147 };
148
149 let Some(xml_name) = name else {
150 return Err(Error::new(
151 meta_span,
152 "`name` is required on non-transparent structs",
153 ));
154 };
155
156 Ok(Self::Compound {
157 inner: Compound::from_fields(
158 fields,
159 &xml_namespace,
160 on_unknown_attribute,
161 on_unknown_child,
162 discard,
163 )?,
164 xml_namespace,
165 xml_name,
166 })
167 }
168 }
169
170 pub(crate) fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
171 match self {
172 Self::Transparent { ty, .. } => Ok(XmlNameMatcher::Custom(quote! {
173 <#ty as ::xso::FromXml>::xml_name_matcher()
174 })),
175 Self::Compound {
176 xml_namespace,
177 xml_name,
178 ..
179 } => Ok(XmlNameMatcher::Specific(
180 xml_namespace.to_token_stream(),
181 xml_name.to_token_stream(),
182 )),
183 }
184 }
185
186 pub(crate) fn make_from_events_statemachine(
187 &self,
188 generics: &mut GenericsInfo,
189 state_ty_ident: &Ident,
190 output_name: &ParentRef,
191 state_prefix: &str,
192 ) -> Result<FromEventsSubmachine> {
193 match self {
194 Self::Transparent { ty, member } => {
195 let from_xml_builder_ty = from_xml_builder_ty(ty.clone());
196 let from_events_fn = from_events_fn(ty.clone());
197 let feed_fn = feed_fn(from_xml_builder_ty.clone());
198
199 let output_cons = match output_name {
200 ParentRef::Named(ref path) => quote! {
201 #path { #member: result }
202 },
203 ParentRef::Unnamed { .. } => quote! {
204 ( result, )
205 },
206 };
207
208 let state_name = quote::format_ident!("{}Default", state_prefix);
209 let builder_data_ident = quote::format_ident!("__xso_data");
210
211 Ok(FromEventsSubmachine {
215 defs: TokenStream::default(),
216 states: vec![
217 State::new_with_builder(
218 state_name.clone(),
219 &builder_data_ident,
220 &from_xml_builder_ty,
221 )
222 .with_impl(quote! {
223 match #feed_fn(&mut #builder_data_ident, ev, ctx)? {
224 ::core::option::Option::Some(result) => {
225 ::core::result::Result::Ok(::core::ops::ControlFlow::Continue(#output_cons))
226 }
227 ::core::option::Option::None => {
228 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
229 #builder_data_ident,
230 }))
231 }
232 }
233 })
234 ],
235 init: quote! {
236 #from_events_fn(name, attrs, ctx).map(|#builder_data_ident| Self::#state_name { #builder_data_ident })
237 },
238 })
239 }
240
241 Self::Compound {
242 ref inner,
243 ref xml_namespace,
244 ref xml_name,
245 } => Ok(inner
246 .make_from_events_statemachine(generics, state_ty_ident, output_name, state_prefix)?
247 .with_augmented_init(|init| {
248 quote! {
249 if name.0 != #xml_namespace || name.1 != #xml_name {
250 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch {
251 name,
252 attrs,
253 })
254 } else {
255 #init
256 }
257 }
258 })),
259 }
260 }
261
262 pub(crate) fn make_as_item_iter_statemachine(
263 &self,
264 generics: &mut GenericsInfo,
265 input_name: &ParentRef,
266 state_ty_ident: &Ident,
267 state_prefix: &str,
268 item_iter_ty_lifetime: &Lifetime,
269 ) -> Result<AsItemsSubmachine> {
270 match self {
271 Self::Transparent { ty, member } => {
272 let item_iter_ty = item_iter_ty(ty.clone(), item_iter_ty_lifetime.clone());
273 let as_xml_iter_fn = as_xml_iter_fn(ty.clone());
274
275 let state_name = quote::format_ident!("{}Default", state_prefix);
276 let iter_ident = quote::format_ident!("__xso_data");
277
278 let destructure = match input_name {
279 ParentRef::Named(ref path) => quote! {
280 #path { #member: #iter_ident }
281 },
282 ParentRef::Unnamed { .. } => quote! {
283 (#iter_ident, )
284 },
285 };
286
287 Ok(AsItemsSubmachine {
291 defs: TokenStream::default(),
292 states: vec![State::new_with_builder(
293 state_name.clone(),
294 &iter_ident,
295 &item_iter_ty,
296 )
297 .with_mut(&iter_ident)
298 .with_impl(quote! {
299 #iter_ident.next().transpose()?
300 })],
301 destructure,
302 init: quote! {
303 #as_xml_iter_fn(#iter_ident).map(|#iter_ident| Self::#state_name { #iter_ident })?
304 },
305 })
306 }
307
308 Self::Compound {
309 ref inner,
310 ref xml_namespace,
311 ref xml_name,
312 } => Ok(inner
313 .make_as_item_iter_statemachine(
314 generics,
315 input_name,
316 state_ty_ident,
317 state_prefix,
318 item_iter_ty_lifetime,
319 )?
320 .with_augmented_init(|init| {
321 quote! {
322 let name = (
323 ::xso::exports::rxml::Namespace::from(#xml_namespace),
324 ::xso::exports::alloc::borrow::Cow::Borrowed(#xml_name),
325 );
326 #init
327 }
328 })),
329 }
330 }
331}
332
333pub(crate) struct StructDef {
335 target_ty_ident: Ident,
337
338 builder_ty_ident: Ident,
340
341 item_iter_ty_ident: Ident,
343
344 debug: bool,
346
347 inner: StructInner,
349
350 deserialize_callback: Option<Path>,
352}
353
354impl StructDef {
355 pub(crate) fn new(ident: &Ident, mut meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> {
357 let builder_ty_ident = match meta.builder.take() {
358 Some(v) => v,
359 None => quote::format_ident!("{}FromXmlBuilder", ident.to_string()),
360 };
361
362 let item_iter_ty_ident = match meta.iterator.take() {
363 Some(v) => v,
364 None => quote::format_ident!("{}AsXmlIterator", ident.to_string()),
365 };
366
367 let debug = meta.debug.take();
368 let deserialize_callback = meta.deserialize_callback.take();
369
370 let inner = StructInner::new(meta, fields)?;
371
372 Ok(Self {
373 inner,
374 target_ty_ident: ident.clone(),
375 builder_ty_ident,
376 item_iter_ty_ident,
377 debug: debug.is_set(),
378 deserialize_callback,
379 })
380 }
381}
382
383impl ItemDef for StructDef {
384 fn make_from_events_builder(
385 &self,
386 vis: &Visibility,
387 generics: &mut GenericsInfo,
388 name_ident: &Ident,
389 attrs_ident: &Ident,
390 ) -> Result<FromXmlParts> {
391 let target_ty_ident = &self.target_ty_ident;
392 let builder_ty_ident = &self.builder_ty_ident;
393 let state_ty_ident = quote::format_ident!("{}State", builder_ty_ident);
394
395 let defs = self
396 .inner
397 .make_from_events_statemachine(
398 generics,
399 &state_ty_ident,
400 &Path::from(target_ty_ident.clone()).into(),
401 "Struct",
402 )?
403 .compile()
404 .render(
405 vis,
406 generics,
407 builder_ty_ident,
408 &state_ty_ident,
409 &generics.ty_with_arguments(target_ty_ident.clone()),
410 self.deserialize_callback.as_ref(),
411 )?;
412
413 Ok(FromXmlParts {
414 defs,
415 from_events_body: quote! {
416 #builder_ty_ident::new(#name_ident, #attrs_ident, ctx)
417 },
418 builder_ty: generics.ty_with_arguments(builder_ty_ident.clone()),
419 name_matcher: self.inner.xml_name_matcher()?,
420 })
421 }
422
423 fn make_as_xml_iter(
424 &self,
425 vis: &Visibility,
426 generics: &mut GenericsInfo,
427 ) -> Result<AsXmlParts> {
428 let target_ty_ident = &self.target_ty_ident;
429 let item_iter_ty_ident = &self.item_iter_ty_ident;
430 let item_iter_ty_lifetime = Lifetime {
431 apostrophe: Span::call_site(),
432 ident: Ident::new("xso_proc_as_xml_iter_lifetime", Span::call_site()),
433 };
434 let target_ty = generics.ty_with_arguments(target_ty_ident.clone());
435 let mut inner_generics = generics.subscope();
436 inner_generics.insert_lifetime(&item_iter_ty_lifetime);
437 let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident);
438
439 let defs = self
440 .inner
441 .make_as_item_iter_statemachine(
442 &mut inner_generics,
443 &Path::from(target_ty_ident.clone()).into(),
444 &state_ty_ident,
445 "Struct",
446 &item_iter_ty_lifetime,
447 )?
448 .compile()
449 .render(
450 vis,
451 &inner_generics,
452 &ref_ty(target_ty, item_iter_ty_lifetime.clone()),
453 &state_ty_ident,
454 &item_iter_ty_lifetime,
455 &item_iter_ty_ident,
456 )?;
457
458 let item_iter_ty = inner_generics.ty_with_arguments(item_iter_ty_ident.clone());
464 generics.extend_bounds(inner_generics.into_new_bounds());
465
466 Ok(AsXmlParts {
467 defs,
468 as_xml_iter_body: quote! {
469 #item_iter_ty_ident::new(self)
470 },
471 item_iter_ty,
472 item_iter_ty_lifetime,
473 })
474 }
475
476 fn debug(&self) -> bool {
477 self.debug
478 }
479}