1use std::collections::HashMap;
10
11use proc_macro2::Span;
12use quote::quote;
13use syn::*;
14
15use crate::common::{AsXmlParts, FromXmlParts, ItemDef};
16use crate::compound::Compound;
17use crate::error_message::ParentRef;
18use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta};
19use crate::state::{AsItemsStateMachine, FromEventsStateMachine};
20use crate::structs::StructInner;
21use crate::types::{ref_ty, ty_from_ident};
22
23struct NameVariant {
26 name: NameRef,
28
29 ident: Ident,
31
32 inner: Compound,
34}
35
36impl NameVariant {
37 fn new(decl: &Variant, enum_namespace: &NamespaceRef) -> Result<Self> {
39 let XmlCompoundMeta {
43 span: meta_span,
44 qname: QNameRef { namespace, name },
45 exhaustive,
46 debug,
47 builder,
48 iterator,
49 on_unknown_attribute,
50 on_unknown_child,
51 transparent,
52 } = XmlCompoundMeta::parse_from_attributes(&decl.attrs)?;
53
54 reject_key!(debug flag not on "enum variants" only on "enums and structs");
55 reject_key!(exhaustive flag not on "enum variants" only on "enums");
56 reject_key!(namespace not on "enum variants" only on "enums and structs");
57 reject_key!(builder not on "enum variants" only on "enums and structs");
58 reject_key!(iterator not on "enum variants" only on "enums and structs");
59 reject_key!(transparent flag not on "named enum variants" only on "structs");
60
61 let Some(name) = name else {
62 return Err(Error::new(meta_span, "`name` is required on enum variants"));
63 };
64
65 Ok(Self {
66 name,
67 ident: decl.ident.clone(),
68 inner: Compound::from_fields(
69 &decl.fields,
70 enum_namespace,
71 on_unknown_attribute,
72 on_unknown_child,
73 )?,
74 })
75 }
76
77 fn make_from_events_statemachine(
78 &self,
79 enum_ident: &Ident,
80 state_ty_ident: &Ident,
81 ) -> Result<FromEventsStateMachine> {
82 let xml_name = &self.name;
83
84 Ok(self
85 .inner
86 .make_from_events_statemachine(
87 state_ty_ident,
88 &ParentRef::Named(Path {
89 leading_colon: None,
90 segments: [
91 PathSegment::from(enum_ident.clone()),
92 self.ident.clone().into(),
93 ]
94 .into_iter()
95 .collect(),
96 }),
97 &self.ident.to_string(),
98 )?
99 .with_augmented_init(|init| {
100 quote! {
101 if name.1 != #xml_name {
102 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch {
103 name,
104 attrs,
105 })
106 } else {
107 #init
108 }
109 }
110 })
111 .compile())
112 }
113
114 fn make_as_item_iter_statemachine(
115 &self,
116 xml_namespace: &NamespaceRef,
117 enum_ident: &Ident,
118 state_ty_ident: &Ident,
119 item_iter_ty_lifetime: &Lifetime,
120 ) -> Result<AsItemsStateMachine> {
121 let xml_name = &self.name;
122
123 Ok(self
124 .inner
125 .make_as_item_iter_statemachine(
126 &ParentRef::Named(Path {
127 leading_colon: None,
128 segments: [
129 PathSegment::from(enum_ident.clone()),
130 self.ident.clone().into(),
131 ]
132 .into_iter()
133 .collect(),
134 }),
135 state_ty_ident,
136 &self.ident.to_string(),
137 &item_iter_ty_lifetime,
138 )?
139 .with_augmented_init(|init| {
140 quote! {
141 let name = (
142 ::xso::exports::rxml::Namespace::from(#xml_namespace),
143 ::xso::exports::alloc::borrow::Cow::Borrowed(#xml_name),
144 );
145 #init
146 }
147 })
148 .compile())
149 }
150}
151
152struct NameSwitchedEnum {
155 namespace: NamespaceRef,
157
158 variants: Vec<NameVariant>,
160
161 exhaustive: bool,
163}
164
165impl NameSwitchedEnum {
166 fn new<'x, I: IntoIterator<Item = &'x Variant>>(
167 namespace: NamespaceRef,
168 exhaustive: Flag,
169 variant_iter: I,
170 ) -> Result<Self> {
171 let mut variants = Vec::new();
172 let mut seen_names = HashMap::new();
173 for variant in variant_iter {
174 let variant = NameVariant::new(variant, &namespace)?;
175 if let Some(other) = seen_names.get(&variant.name) {
176 return Err(Error::new_spanned(
177 variant.name,
178 format!(
179 "duplicate `name` in enum: variants {} and {} have the same XML name",
180 other, variant.ident
181 ),
182 ));
183 }
184 seen_names.insert(variant.name.clone(), variant.ident.clone());
185 variants.push(variant);
186 }
187
188 Ok(Self {
189 namespace,
190 variants,
191 exhaustive: exhaustive.is_set(),
192 })
193 }
194
195 fn make_from_events_statemachine(
197 &self,
198 target_ty_ident: &Ident,
199 state_ty_ident: &Ident,
200 ) -> Result<FromEventsStateMachine> {
201 let xml_namespace = &self.namespace;
202
203 let mut statemachine = FromEventsStateMachine::new();
204 for variant in self.variants.iter() {
205 statemachine
206 .merge(variant.make_from_events_statemachine(target_ty_ident, state_ty_ident)?);
207 }
208
209 statemachine.set_pre_init(quote! {
210 if name.0 != #xml_namespace {
211 return ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch {
212 name,
213 attrs,
214 })
215 }
216 });
217
218 if self.exhaustive {
219 let mismatch_err = format!("This is not a {} element.", target_ty_ident);
220 statemachine.set_fallback(quote! {
221 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(
222 ::xso::error::Error::Other(#mismatch_err),
223 ))
224 })
225 }
226
227 Ok(statemachine)
228 }
229
230 fn make_as_item_iter_statemachine(
232 &self,
233 target_ty_ident: &Ident,
234 state_ty_ident: &Ident,
235 item_iter_ty_lifetime: &Lifetime,
236 ) -> Result<AsItemsStateMachine> {
237 let mut statemachine = AsItemsStateMachine::new();
238 for variant in self.variants.iter() {
239 statemachine.merge(variant.make_as_item_iter_statemachine(
240 &self.namespace,
241 target_ty_ident,
242 state_ty_ident,
243 item_iter_ty_lifetime,
244 )?);
245 }
246
247 Ok(statemachine)
248 }
249}
250
251struct DynamicVariant {
253 ident: Ident,
255
256 inner: StructInner,
258}
259
260impl DynamicVariant {
261 fn new(variant: &Variant) -> Result<Self> {
262 let ident = variant.ident.clone();
263 let meta = XmlCompoundMeta::parse_from_attributes(&variant.attrs)?;
264
265 let XmlCompoundMeta {
269 span: _,
270 qname: _, ref exhaustive,
272 ref debug,
273 ref builder,
274 ref iterator,
275 on_unknown_attribute: _, on_unknown_child: _, transparent: _, } = meta;
279
280 reject_key!(debug flag not on "enum variants" only on "enums and structs");
281 reject_key!(exhaustive flag not on "enum variants" only on "enums");
282 reject_key!(builder not on "enum variants" only on "enums and structs");
283 reject_key!(iterator not on "enum variants" only on "enums and structs");
284
285 let inner = StructInner::new(meta, &variant.fields)?;
286 Ok(Self { ident, inner })
287 }
288}
289
290struct DynamicEnum {
293 variants: Vec<DynamicVariant>,
295}
296
297impl DynamicEnum {
298 fn new<'x, I: IntoIterator<Item = &'x Variant>>(variant_iter: I) -> Result<Self> {
299 let mut variants = Vec::new();
300 for variant in variant_iter {
301 variants.push(DynamicVariant::new(variant)?);
302 }
303
304 Ok(Self { variants })
305 }
306
307 fn make_from_events_statemachine(
309 &self,
310 target_ty_ident: &Ident,
311 state_ty_ident: &Ident,
312 ) -> Result<FromEventsStateMachine> {
313 let mut statemachine = FromEventsStateMachine::new();
314 for variant in self.variants.iter() {
315 let submachine = variant.inner.make_from_events_statemachine(
316 state_ty_ident,
317 &ParentRef::Named(Path {
318 leading_colon: None,
319 segments: [
320 PathSegment::from(target_ty_ident.clone()),
321 variant.ident.clone().into(),
322 ]
323 .into_iter()
324 .collect(),
325 }),
326 &variant.ident.to_string(),
327 )?;
328
329 statemachine.merge(submachine.compile());
330 }
331
332 Ok(statemachine)
333 }
334
335 fn make_as_item_iter_statemachine(
337 &self,
338 target_ty_ident: &Ident,
339 state_ty_ident: &Ident,
340 item_iter_ty_lifetime: &Lifetime,
341 ) -> Result<AsItemsStateMachine> {
342 let mut statemachine = AsItemsStateMachine::new();
343 for variant in self.variants.iter() {
344 let submachine = variant.inner.make_as_item_iter_statemachine(
345 &ParentRef::Named(Path {
346 leading_colon: None,
347 segments: [
348 PathSegment::from(target_ty_ident.clone()),
349 variant.ident.clone().into(),
350 ]
351 .into_iter()
352 .collect(),
353 }),
354 state_ty_ident,
355 &variant.ident.to_string(),
356 item_iter_ty_lifetime,
357 )?;
358
359 statemachine.merge(submachine.compile());
360 }
361
362 Ok(statemachine)
363 }
364}
365
366enum EnumInner {
368 NameSwitched(NameSwitchedEnum),
371
372 Dynamic(DynamicEnum),
374}
375
376impl EnumInner {
377 fn new<'x, I: IntoIterator<Item = &'x Variant>>(
378 meta: XmlCompoundMeta,
379 variant_iter: I,
380 ) -> Result<Self> {
381 let XmlCompoundMeta {
385 span: _,
386 qname: QNameRef { namespace, name },
387 exhaustive,
388 debug,
389 builder,
390 iterator,
391 on_unknown_attribute,
392 on_unknown_child,
393 transparent,
394 } = meta;
395
396 assert!(builder.is_none());
400 assert!(iterator.is_none());
401 assert!(!debug.is_set());
402
403 reject_key!(name not on "enums" only on "their variants");
404 reject_key!(transparent flag not on "enums" only on "structs");
405 reject_key!(on_unknown_attribute not on "enums" only on "enum variants and structs");
406 reject_key!(on_unknown_child not on "enums" only on "enum variants and structs");
407
408 if let Some(namespace) = namespace {
409 Ok(Self::NameSwitched(NameSwitchedEnum::new(
410 namespace,
411 exhaustive,
412 variant_iter,
413 )?))
414 } else {
415 reject_key!(exhaustive flag not on "dynamic enums" only on "name-switched enums");
416 Ok(Self::Dynamic(DynamicEnum::new(variant_iter)?))
417 }
418 }
419
420 fn make_from_events_statemachine(
422 &self,
423 target_ty_ident: &Ident,
424 state_ty_ident: &Ident,
425 ) -> Result<FromEventsStateMachine> {
426 match self {
427 Self::NameSwitched(ref inner) => {
428 inner.make_from_events_statemachine(target_ty_ident, state_ty_ident)
429 }
430 Self::Dynamic(ref inner) => {
431 inner.make_from_events_statemachine(target_ty_ident, state_ty_ident)
432 }
433 }
434 }
435
436 fn make_as_item_iter_statemachine(
438 &self,
439 target_ty_ident: &Ident,
440 state_ty_ident: &Ident,
441 item_iter_ty_lifetime: &Lifetime,
442 ) -> Result<AsItemsStateMachine> {
443 match self {
444 Self::NameSwitched(ref inner) => inner.make_as_item_iter_statemachine(
445 target_ty_ident,
446 state_ty_ident,
447 item_iter_ty_lifetime,
448 ),
449 Self::Dynamic(ref inner) => inner.make_as_item_iter_statemachine(
450 target_ty_ident,
451 state_ty_ident,
452 item_iter_ty_lifetime,
453 ),
454 }
455 }
456}
457
458pub(crate) struct EnumDef {
460 inner: EnumInner,
462
463 target_ty_ident: Ident,
465
466 builder_ty_ident: Ident,
468
469 item_iter_ty_ident: Ident,
471
472 debug: bool,
474}
475
476impl EnumDef {
477 pub(crate) fn new<'x, I: IntoIterator<Item = &'x Variant>>(
479 ident: &Ident,
480 mut meta: XmlCompoundMeta,
481 variant_iter: I,
482 ) -> Result<Self> {
483 let builder_ty_ident = match meta.builder.take() {
484 Some(v) => v,
485 None => quote::format_ident!("{}FromXmlBuilder", ident.to_string()),
486 };
487
488 let item_iter_ty_ident = match meta.iterator.take() {
489 Some(v) => v,
490 None => quote::format_ident!("{}AsXmlIterator", ident.to_string()),
491 };
492
493 let debug = meta.debug.take().is_set();
494
495 Ok(Self {
496 inner: EnumInner::new(meta, variant_iter)?,
497 target_ty_ident: ident.clone(),
498 builder_ty_ident,
499 item_iter_ty_ident,
500 debug,
501 })
502 }
503}
504
505impl ItemDef for EnumDef {
506 fn make_from_events_builder(
507 &self,
508 vis: &Visibility,
509 name_ident: &Ident,
510 attrs_ident: &Ident,
511 ) -> Result<FromXmlParts> {
512 let target_ty_ident = &self.target_ty_ident;
513 let builder_ty_ident = &self.builder_ty_ident;
514 let state_ty_ident = quote::format_ident!("{}State", builder_ty_ident);
515
516 let defs = self
517 .inner
518 .make_from_events_statemachine(target_ty_ident, &state_ty_ident)?
519 .render(
520 vis,
521 builder_ty_ident,
522 &state_ty_ident,
523 &TypePath {
524 qself: None,
525 path: target_ty_ident.clone().into(),
526 }
527 .into(),
528 )?;
529
530 Ok(FromXmlParts {
531 defs,
532 from_events_body: quote! {
533 #builder_ty_ident::new(#name_ident, #attrs_ident)
534 },
535 builder_ty_ident: builder_ty_ident.clone(),
536 })
537 }
538
539 fn make_as_xml_iter(&self, vis: &Visibility) -> Result<AsXmlParts> {
540 let target_ty_ident = &self.target_ty_ident;
541 let item_iter_ty_ident = &self.item_iter_ty_ident;
542 let item_iter_ty_lifetime = Lifetime {
543 apostrophe: Span::call_site(),
544 ident: Ident::new("xso_proc_as_xml_iter_lifetime", Span::call_site()),
545 };
546 let item_iter_ty = Type::Path(TypePath {
547 qself: None,
548 path: Path {
549 leading_colon: None,
550 segments: [PathSegment {
551 ident: item_iter_ty_ident.clone(),
552 arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
553 colon2_token: None,
554 lt_token: token::Lt {
555 spans: [Span::call_site()],
556 },
557 args: [GenericArgument::Lifetime(item_iter_ty_lifetime.clone())]
558 .into_iter()
559 .collect(),
560 gt_token: token::Gt {
561 spans: [Span::call_site()],
562 },
563 }),
564 }]
565 .into_iter()
566 .collect(),
567 },
568 });
569 let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident);
570
571 let defs = self
572 .inner
573 .make_as_item_iter_statemachine(
574 target_ty_ident,
575 &state_ty_ident,
576 &item_iter_ty_lifetime,
577 )?
578 .render(
579 vis,
580 &ref_ty(
581 ty_from_ident(target_ty_ident.clone()).into(),
582 item_iter_ty_lifetime.clone(),
583 ),
584 &state_ty_ident,
585 &item_iter_ty_lifetime,
586 &item_iter_ty,
587 )?;
588
589 Ok(AsXmlParts {
590 defs,
591 as_xml_iter_body: quote! {
592 #item_iter_ty_ident::new(self)
593 },
594 item_iter_ty,
595 item_iter_ty_lifetime,
596 })
597 }
598
599 fn debug(&self) -> bool {
600 self.debug
601 }
602}