1use std::collections::HashMap;
10
11use proc_macro2::{Span, TokenStream};
12use quote::{quote, ToTokens};
13use syn::*;
14
15use crate::common::{AsXmlParts, FromXmlParts, ItemDef, XmlNameMatcher};
16use crate::compound::Compound;
17use crate::error_message::ParentRef;
18use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta};
19use crate::state::{AsItemsStateMachine, FromEventsMatchMode, 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 discard,
53 deserialize_callback,
54 attribute,
55 value,
56 } = XmlCompoundMeta::parse_from_attributes(&decl.attrs)?;
57
58 reject_key!(debug flag not on "enum variants" only on "enums and structs");
59 reject_key!(exhaustive flag not on "enum variants" only on "enums");
60 reject_key!(namespace not on "enum variants" only on "enums and structs");
61 reject_key!(builder not on "enum variants" only on "enums and structs");
62 reject_key!(iterator not on "enum variants" only on "enums and structs");
63 reject_key!(transparent flag not on "named enum variants" only on "structs");
64 reject_key!(deserialize_callback not on "enum variants" only on "enums and structs");
65 reject_key!(attribute not on "enum variants" only on "attribute-switched enums");
66 reject_key!(value not on "name-switched enum variants" only on "attribute-switched enum variants");
67
68 let Some(name) = name else {
69 return Err(Error::new(
70 meta_span,
71 "`name` is required on name-switched enum variants",
72 ));
73 };
74
75 Ok(Self {
76 name,
77 ident: decl.ident.clone(),
78 inner: Compound::from_fields(
79 &decl.fields,
80 enum_namespace,
81 on_unknown_attribute,
82 on_unknown_child,
83 discard,
84 )?,
85 })
86 }
87
88 fn make_from_events_statemachine(
89 &self,
90 enum_ident: &Ident,
91 state_ty_ident: &Ident,
92 ) -> Result<FromEventsStateMachine> {
93 let xml_name = &self.name;
94
95 Ok(self
96 .inner
97 .make_from_events_statemachine(
98 state_ty_ident,
99 &ParentRef::Named(Path {
100 leading_colon: None,
101 segments: [
102 PathSegment::from(enum_ident.clone()),
103 self.ident.clone().into(),
104 ]
105 .into_iter()
106 .collect(),
107 }),
108 &self.ident.to_string(),
109 )?
110 .with_augmented_init(|init| {
111 quote! {
112 if name.1 != #xml_name {
113 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch {
114 name,
115 attrs,
116 })
117 } else {
118 #init
119 }
120 }
121 })
122 .compile())
123 }
124
125 fn make_as_item_iter_statemachine(
126 &self,
127 xml_namespace: &NamespaceRef,
128 enum_ident: &Ident,
129 state_ty_ident: &Ident,
130 item_iter_ty_lifetime: &Lifetime,
131 ) -> Result<AsItemsStateMachine> {
132 let xml_name = &self.name;
133
134 Ok(self
135 .inner
136 .make_as_item_iter_statemachine(
137 &ParentRef::Named(Path {
138 leading_colon: None,
139 segments: [
140 PathSegment::from(enum_ident.clone()),
141 self.ident.clone().into(),
142 ]
143 .into_iter()
144 .collect(),
145 }),
146 state_ty_ident,
147 &self.ident.to_string(),
148 item_iter_ty_lifetime,
149 )?
150 .with_augmented_init(|init| {
151 quote! {
152 let name = (
153 ::xso::exports::rxml::Namespace::from(#xml_namespace),
154 ::xso::exports::alloc::borrow::Cow::Borrowed(#xml_name),
155 );
156 #init
157 }
158 })
159 .compile())
160 }
161}
162
163struct NameSwitchedEnum {
166 namespace: NamespaceRef,
168
169 variants: Vec<NameVariant>,
171
172 exhaustive: bool,
174}
175
176impl NameSwitchedEnum {
177 fn new<'x, I: IntoIterator<Item = &'x Variant>>(
178 namespace: NamespaceRef,
179 exhaustive: Flag,
180 variant_iter: I,
181 ) -> Result<Self> {
182 let mut variants = Vec::new();
183 let mut seen_names = HashMap::new();
184 for variant in variant_iter {
185 let variant = NameVariant::new(variant, &namespace)?;
186 if let Some(other) = seen_names.get(&variant.name) {
187 return Err(Error::new_spanned(
188 variant.name,
189 format!(
190 "duplicate `name` in enum: variants {} and {} have the same XML name",
191 other, variant.ident
192 ),
193 ));
194 }
195 seen_names.insert(variant.name.clone(), variant.ident.clone());
196 variants.push(variant);
197 }
198
199 Ok(Self {
200 namespace,
201 variants,
202 exhaustive: exhaustive.is_set(),
203 })
204 }
205
206 fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
211 Ok(XmlNameMatcher::InNamespace(
212 self.namespace.to_token_stream(),
213 ))
214 }
215
216 fn make_from_events_statemachine(
218 &self,
219 target_ty_ident: &Ident,
220 state_ty_ident: &Ident,
221 ) -> Result<FromEventsStateMachine> {
222 let xml_namespace = &self.namespace;
223
224 let mut statemachine = FromEventsStateMachine::new();
225 for variant in self.variants.iter() {
226 statemachine
227 .merge(variant.make_from_events_statemachine(target_ty_ident, state_ty_ident)?);
228 }
229
230 statemachine.set_pre_init(quote! {
231 if name.0 != #xml_namespace {
232 return ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch {
233 name,
234 attrs,
235 })
236 }
237 });
238
239 if self.exhaustive {
240 let mismatch_err = format!("This is not a {} element.", target_ty_ident);
241 statemachine.set_fallback(quote! {
242 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(
243 ::xso::error::Error::Other(#mismatch_err),
244 ))
245 })
246 }
247
248 Ok(statemachine)
249 }
250
251 fn make_as_item_iter_statemachine(
253 &self,
254 target_ty_ident: &Ident,
255 state_ty_ident: &Ident,
256 item_iter_ty_lifetime: &Lifetime,
257 ) -> Result<AsItemsStateMachine> {
258 let mut statemachine = AsItemsStateMachine::new();
259 for variant in self.variants.iter() {
260 statemachine.merge(variant.make_as_item_iter_statemachine(
261 &self.namespace,
262 target_ty_ident,
263 state_ty_ident,
264 item_iter_ty_lifetime,
265 )?);
266 }
267
268 Ok(statemachine)
269 }
270}
271
272struct ValueVariant {
275 value: String,
277
278 ident: Ident,
280
281 inner: Compound,
283}
284
285impl ValueVariant {
286 fn new(decl: &Variant, enum_namespace: &NamespaceRef) -> Result<Self> {
288 let XmlCompoundMeta {
292 span: meta_span,
293 qname: QNameRef { namespace, name },
294 exhaustive,
295 debug,
296 builder,
297 iterator,
298 on_unknown_attribute,
299 on_unknown_child,
300 transparent,
301 discard,
302 deserialize_callback,
303 attribute,
304 value,
305 } = XmlCompoundMeta::parse_from_attributes(&decl.attrs)?;
306
307 reject_key!(debug flag not on "enum variants" only on "enums and structs");
308 reject_key!(exhaustive flag not on "enum variants" only on "enums");
309 reject_key!(namespace not on "enum variants" only on "enums and structs");
310 reject_key!(name not on "attribute-switched enum variants" only on "enums, structs and name-switched enum variants");
311 reject_key!(builder not on "enum variants" only on "enums and structs");
312 reject_key!(iterator not on "enum variants" only on "enums and structs");
313 reject_key!(transparent flag not on "named enum variants" only on "structs");
314 reject_key!(deserialize_callback not on "enum variants" only on "enums and structs");
315 reject_key!(attribute not on "enum variants" only on "attribute-switched enums");
316
317 let Some(value) = value else {
318 return Err(Error::new(
319 meta_span,
320 "`value` is required on attribute-switched enum variants",
321 ));
322 };
323
324 Ok(Self {
325 value: value.value(),
326 ident: decl.ident.clone(),
327 inner: Compound::from_fields(
328 &decl.fields,
329 enum_namespace,
330 on_unknown_attribute,
331 on_unknown_child,
332 discard,
333 )?,
334 })
335 }
336
337 fn make_from_events_statemachine(
338 &self,
339 enum_ident: &Ident,
340 state_ty_ident: &Ident,
341 ) -> Result<FromEventsStateMachine> {
342 let value = &self.value;
343
344 Ok(self
345 .inner
346 .make_from_events_statemachine(
347 state_ty_ident,
348 &ParentRef::Named(Path {
349 leading_colon: None,
350 segments: [
351 PathSegment::from(enum_ident.clone()),
352 self.ident.clone().into(),
353 ]
354 .into_iter()
355 .collect(),
356 }),
357 &self.ident.to_string(),
358 )?
359 .with_augmented_init(|init| {
360 quote! {
361 #value => { #init },
362 }
363 })
364 .compile())
365 }
366
367 fn make_as_item_iter_statemachine(
368 &self,
369 elem_namespace: &NamespaceRef,
370 elem_name: &NameRef,
371 attr_namespace: &TokenStream,
372 attr_name: &NameRef,
373 enum_ident: &Ident,
374 state_ty_ident: &Ident,
375 item_iter_ty_lifetime: &Lifetime,
376 ) -> Result<AsItemsStateMachine> {
377 let attr_value = &self.value;
378
379 Ok(self
380 .inner
381 .make_as_item_iter_statemachine(
382 &ParentRef::Named(Path {
383 leading_colon: None,
384 segments: [
385 PathSegment::from(enum_ident.clone()),
386 self.ident.clone().into(),
387 ]
388 .into_iter()
389 .collect(),
390 }),
391 state_ty_ident,
392 &self.ident.to_string(),
393 item_iter_ty_lifetime,
394 )?
395 .with_augmented_init(|init| {
396 quote! {
397 let name = (
398 ::xso::exports::rxml::Namespace::from(#elem_namespace),
399 ::xso::exports::alloc::borrow::Cow::Borrowed(#elem_name),
400 );
401 #init
402 }
403 })
404 .with_extra_header_state(
405 quote::format_ident!("{}Discriminator", &self.ident.to_string()),
408 quote! {
409 ::xso::Item::Attribute(
410 #attr_namespace,
411 ::xso::exports::alloc::borrow::Cow::Borrowed(#attr_name),
412 ::xso::exports::alloc::borrow::Cow::Borrowed(#attr_value),
413 )
414 },
415 )
416 .compile())
417 }
418}
419
420struct AttributeSwitchedEnum {
423 elem_namespace: NamespaceRef,
425
426 elem_name: NameRef,
428
429 attr_namespace: Option<NamespaceRef>,
432
433 attr_name: NameRef,
435
436 variants: Vec<ValueVariant>,
438}
439
440impl AttributeSwitchedEnum {
441 fn new<'x, I: IntoIterator<Item = &'x Variant>>(
442 elem_namespace: NamespaceRef,
443 elem_name: NameRef,
444 attr_namespace: Option<NamespaceRef>,
445 attr_name: NameRef,
446 variant_iter: I,
447 ) -> Result<Self> {
448 let mut variants = Vec::new();
449 let mut seen_values = HashMap::new();
450 for variant in variant_iter {
451 let variant = ValueVariant::new(variant, &elem_namespace)?;
452 if let Some(other) = seen_values.get(&variant.value) {
453 return Err(Error::new_spanned(
454 variant.value,
455 format!(
456 "duplicate `value` in enum: variants {} and {} have the same attribute value",
457 other, variant.ident
458 ),
459 ));
460 }
461 seen_values.insert(variant.value.clone(), variant.ident.clone());
462 variants.push(variant);
463 }
464
465 Ok(Self {
466 elem_namespace,
467 elem_name,
468 attr_namespace,
469 attr_name,
470 variants,
471 })
472 }
473
474 fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
479 Ok(XmlNameMatcher::Specific(
480 self.elem_namespace.to_token_stream(),
481 self.elem_name.to_token_stream(),
482 ))
483 }
484
485 fn make_from_events_statemachine(
487 &self,
488 target_ty_ident: &Ident,
489 state_ty_ident: &Ident,
490 ) -> Result<FromEventsStateMachine> {
491 let elem_namespace = &self.elem_namespace;
492 let elem_name = &self.elem_name;
493 let attr_namespace = match self.attr_namespace.as_ref() {
494 Some(v) => v.to_token_stream(),
495 None => quote! {
496 ::xso::exports::rxml::Namespace::none()
497 },
498 };
499 let attr_name = &self.attr_name;
500
501 let mut statemachine = FromEventsStateMachine::new();
502 for variant in self.variants.iter() {
503 statemachine
504 .merge(variant.make_from_events_statemachine(target_ty_ident, state_ty_ident)?);
505 }
506
507 statemachine.set_pre_init(quote! {
508 let attr = {
509 if name.0 != #elem_namespace || name.1 != #elem_name {
510 return ::core::result::Result::Err(
511 ::xso::error::FromEventsError::Mismatch {
512 name,
513 attrs,
514 },
515 );
516 }
517
518 let ::core::option::Option::Some(attr) = attrs.remove(#attr_namespace, #attr_name) else {
519 return ::core::result::Result::Err(
520 ::xso::error::FromEventsError::Invalid(
521 ::xso::error::Error::Other("Missing discriminator attribute.")
522 ),
523 );
524 };
525
526 attr
527 };
528 });
529 statemachine.set_fallback(quote! {
530 ::core::result::Result::Err(
531 ::xso::error::FromEventsError::Invalid(
532 ::xso::error::Error::Other("Unknown value for discriminator attribute.")
533 ),
534 )
535 });
536 statemachine.set_match_mode(FromEventsMatchMode::Matched {
537 expr: quote! { attr.as_str() },
538 });
539
540 Ok(statemachine)
541 }
542
543 fn make_as_item_iter_statemachine(
545 &self,
546 target_ty_ident: &Ident,
547 state_ty_ident: &Ident,
548 item_iter_ty_lifetime: &Lifetime,
549 ) -> Result<AsItemsStateMachine> {
550 let attr_namespace = match self.attr_namespace.as_ref() {
551 Some(v) => v.to_token_stream(),
552 None => quote! {
553 ::xso::exports::rxml::Namespace::NONE
554 },
555 };
556
557 let mut statemachine = AsItemsStateMachine::new();
558 for variant in self.variants.iter() {
559 statemachine.merge(variant.make_as_item_iter_statemachine(
560 &self.elem_namespace,
561 &self.elem_name,
562 &attr_namespace,
563 &self.attr_name,
564 target_ty_ident,
565 state_ty_ident,
566 item_iter_ty_lifetime,
567 )?);
568 }
569
570 Ok(statemachine)
571 }
572}
573
574struct DynamicVariant {
576 ident: Ident,
578
579 inner: StructInner,
581}
582
583impl DynamicVariant {
584 fn new(variant: &Variant) -> Result<Self> {
585 let ident = variant.ident.clone();
586 let meta = XmlCompoundMeta::parse_from_attributes(&variant.attrs)?;
587
588 let XmlCompoundMeta {
592 span: _,
593 qname: _, ref exhaustive,
595 ref debug,
596 ref builder,
597 ref iterator,
598 on_unknown_attribute: _, on_unknown_child: _, transparent: _, discard: _, ref deserialize_callback,
603 ref attribute,
604 ref value,
605 } = meta;
606
607 reject_key!(debug flag not on "enum variants" only on "enums and structs");
608 reject_key!(exhaustive flag not on "enum variants" only on "enums");
609 reject_key!(builder not on "enum variants" only on "enums and structs");
610 reject_key!(iterator not on "enum variants" only on "enums and structs");
611 reject_key!(deserialize_callback not on "enum variants" only on "enums and structs");
612 reject_key!(attribute not on "enum variants" only on "attribute-switched enums");
613 reject_key!(value not on "dynamic enum variants" only on "attribute-switched enum variants");
614
615 let inner = StructInner::new(meta, &variant.fields)?;
616 Ok(Self { ident, inner })
617 }
618}
619
620struct DynamicEnum {
623 variants: Vec<DynamicVariant>,
625}
626
627impl DynamicEnum {
628 fn new<'x, I: IntoIterator<Item = &'x Variant>>(variant_iter: I) -> Result<Self> {
629 let mut variants = Vec::new();
630 for variant in variant_iter {
631 variants.push(DynamicVariant::new(variant)?);
632 }
633
634 Ok(Self { variants })
635 }
636
637 fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
642 let mut iter = self.variants.iter();
643 let Some(first) = iter.next() else {
644 return Ok(XmlNameMatcher::Custom(quote! {
648 ::xso::fromxml::XmlNameMatcher::<'static>::Specific("", "")
649 }));
650 };
651
652 let first_matcher = first.inner.xml_name_matcher()?;
653 let mut composition = quote! { #first_matcher };
654 for variant in iter {
655 let next_matcher = variant.inner.xml_name_matcher()?;
656 composition.extend(quote! { .superset(#next_matcher) });
657 }
658
659 Ok(XmlNameMatcher::Custom(composition))
660 }
661
662 fn make_from_events_statemachine(
664 &self,
665 target_ty_ident: &Ident,
666 state_ty_ident: &Ident,
667 ) -> Result<FromEventsStateMachine> {
668 let mut statemachine = FromEventsStateMachine::new();
669 for variant in self.variants.iter() {
670 let submachine = variant.inner.make_from_events_statemachine(
671 state_ty_ident,
672 &ParentRef::Named(Path {
673 leading_colon: None,
674 segments: [
675 PathSegment::from(target_ty_ident.clone()),
676 variant.ident.clone().into(),
677 ]
678 .into_iter()
679 .collect(),
680 }),
681 &variant.ident.to_string(),
682 )?;
683
684 statemachine.merge(submachine.compile());
685 }
686
687 Ok(statemachine)
688 }
689
690 fn make_as_item_iter_statemachine(
692 &self,
693 target_ty_ident: &Ident,
694 state_ty_ident: &Ident,
695 item_iter_ty_lifetime: &Lifetime,
696 ) -> Result<AsItemsStateMachine> {
697 let mut statemachine = AsItemsStateMachine::new();
698 for variant in self.variants.iter() {
699 let submachine = variant.inner.make_as_item_iter_statemachine(
700 &ParentRef::Named(Path {
701 leading_colon: None,
702 segments: [
703 PathSegment::from(target_ty_ident.clone()),
704 variant.ident.clone().into(),
705 ]
706 .into_iter()
707 .collect(),
708 }),
709 state_ty_ident,
710 &variant.ident.to_string(),
711 item_iter_ty_lifetime,
712 )?;
713
714 statemachine.merge(submachine.compile());
715 }
716
717 Ok(statemachine)
718 }
719}
720
721enum EnumInner {
723 NameSwitched(NameSwitchedEnum),
726
727 AttributeSwitched(AttributeSwitchedEnum),
730
731 Dynamic(DynamicEnum),
733}
734
735impl EnumInner {
736 fn new<'x, I: IntoIterator<Item = &'x Variant>>(
737 meta: XmlCompoundMeta,
738 variant_iter: I,
739 ) -> Result<Self> {
740 let XmlCompoundMeta {
744 span,
745 qname: QNameRef { namespace, name },
746 exhaustive,
747 debug,
748 builder,
749 iterator,
750 on_unknown_attribute,
751 on_unknown_child,
752 transparent,
753 discard,
754 deserialize_callback,
755 attribute,
756 value,
757 } = meta;
758
759 assert!(builder.is_none());
763 assert!(iterator.is_none());
764 assert!(!debug.is_set());
765 assert!(deserialize_callback.is_none());
766
767 reject_key!(transparent flag not on "enums" only on "structs");
768 reject_key!(on_unknown_attribute not on "enums" only on "enum variants and structs");
769 reject_key!(on_unknown_child not on "enums" only on "enum variants and structs");
770 reject_key!(discard vec not on "enums" only on "enum variants and structs");
771 reject_key!(value not on "enums" only on "attribute-switched enum variants");
772
773 if let Some(attribute) = attribute {
774 let Some(attr_name) = attribute.qname.name else {
775 return Err(Error::new(
776 attribute.span,
777 "missing `name` for `attribute` key",
778 ));
779 };
780
781 let attr_namespace = attribute.qname.namespace;
782
783 let Some(elem_namespace) = namespace else {
784 let mut error =
785 Error::new(span, "missing `namespace` key on attribute-switched enum");
786 error.combine(Error::new(
787 attribute.span,
788 "enum is attribute-switched because of the `attribute` key here",
789 ));
790 return Err(error);
791 };
792
793 let Some(elem_name) = name else {
794 let mut error = Error::new(span, "missing `name` key on attribute-switched enum");
795 error.combine(Error::new(
796 attribute.span,
797 "enum is attribute-switched because of the `attribute` key here",
798 ));
799 return Err(error);
800 };
801
802 if !exhaustive.is_set() {
803 return Err(Error::new(
804 span,
805 "attribute-switched enums must be marked as `exhaustive`. non-exhaustive attribute-switched enums are not supported."
806 ));
807 }
808
809 Ok(Self::AttributeSwitched(AttributeSwitchedEnum::new(
810 elem_namespace,
811 elem_name,
812 attr_namespace,
813 attr_name,
814 variant_iter,
815 )?))
816 } else if let Some(namespace) = namespace {
817 reject_key!(name not on "name-switched enums" only on "their variants");
818 Ok(Self::NameSwitched(NameSwitchedEnum::new(
819 namespace,
820 exhaustive,
821 variant_iter,
822 )?))
823 } else {
824 reject_key!(name not on "dynamic enums" only on "their variants");
825 reject_key!(exhaustive flag not on "dynamic enums" only on "name-switched enums");
826 Ok(Self::Dynamic(DynamicEnum::new(variant_iter)?))
827 }
828 }
829
830 fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
832 match self {
833 Self::NameSwitched(ref inner) => inner.xml_name_matcher(),
834 Self::AttributeSwitched(ref inner) => inner.xml_name_matcher(),
835 Self::Dynamic(ref inner) => inner.xml_name_matcher(),
836 }
837 }
838
839 fn make_from_events_statemachine(
841 &self,
842 target_ty_ident: &Ident,
843 state_ty_ident: &Ident,
844 ) -> Result<FromEventsStateMachine> {
845 match self {
846 Self::NameSwitched(ref inner) => {
847 inner.make_from_events_statemachine(target_ty_ident, state_ty_ident)
848 }
849 Self::AttributeSwitched(ref inner) => {
850 inner.make_from_events_statemachine(target_ty_ident, state_ty_ident)
851 }
852 Self::Dynamic(ref inner) => {
853 inner.make_from_events_statemachine(target_ty_ident, state_ty_ident)
854 }
855 }
856 }
857
858 fn make_as_item_iter_statemachine(
860 &self,
861 target_ty_ident: &Ident,
862 state_ty_ident: &Ident,
863 item_iter_ty_lifetime: &Lifetime,
864 ) -> Result<AsItemsStateMachine> {
865 match self {
866 Self::NameSwitched(ref inner) => inner.make_as_item_iter_statemachine(
867 target_ty_ident,
868 state_ty_ident,
869 item_iter_ty_lifetime,
870 ),
871 Self::AttributeSwitched(ref inner) => inner.make_as_item_iter_statemachine(
872 target_ty_ident,
873 state_ty_ident,
874 item_iter_ty_lifetime,
875 ),
876 Self::Dynamic(ref inner) => inner.make_as_item_iter_statemachine(
877 target_ty_ident,
878 state_ty_ident,
879 item_iter_ty_lifetime,
880 ),
881 }
882 }
883}
884
885pub(crate) struct EnumDef {
887 inner: EnumInner,
889
890 target_ty_ident: Ident,
892
893 builder_ty_ident: Ident,
895
896 item_iter_ty_ident: Ident,
898
899 debug: bool,
901
902 deserialize_callback: Option<Path>,
904}
905
906impl EnumDef {
907 pub(crate) fn new<'x, I: IntoIterator<Item = &'x Variant>>(
909 ident: &Ident,
910 mut meta: XmlCompoundMeta,
911 variant_iter: I,
912 ) -> Result<Self> {
913 let builder_ty_ident = match meta.builder.take() {
914 Some(v) => v,
915 None => quote::format_ident!("{}FromXmlBuilder", ident.to_string()),
916 };
917
918 let item_iter_ty_ident = match meta.iterator.take() {
919 Some(v) => v,
920 None => quote::format_ident!("{}AsXmlIterator", ident.to_string()),
921 };
922
923 let debug = meta.debug.take().is_set();
924 let deserialize_callback = meta.deserialize_callback.take();
925
926 Ok(Self {
927 inner: EnumInner::new(meta, variant_iter)?,
928 target_ty_ident: ident.clone(),
929 builder_ty_ident,
930 item_iter_ty_ident,
931 debug,
932 deserialize_callback,
933 })
934 }
935}
936
937impl ItemDef for EnumDef {
938 fn make_from_events_builder(
939 &self,
940 vis: &Visibility,
941 name_ident: &Ident,
942 attrs_ident: &Ident,
943 ) -> Result<FromXmlParts> {
944 let target_ty_ident = &self.target_ty_ident;
945 let builder_ty_ident = &self.builder_ty_ident;
946 let state_ty_ident = quote::format_ident!("{}State", builder_ty_ident);
947
948 let defs = self
949 .inner
950 .make_from_events_statemachine(target_ty_ident, &state_ty_ident)?
951 .render(
952 vis,
953 builder_ty_ident,
954 &state_ty_ident,
955 &TypePath {
956 qself: None,
957 path: target_ty_ident.clone().into(),
958 }
959 .into(),
960 self.deserialize_callback.as_ref(),
961 )?;
962
963 Ok(FromXmlParts {
964 defs,
965 from_events_body: quote! {
966 #builder_ty_ident::new(#name_ident, #attrs_ident, ctx)
967 },
968 builder_ty_ident: builder_ty_ident.clone(),
969 name_matcher: self.inner.xml_name_matcher()?,
970 })
971 }
972
973 fn make_as_xml_iter(&self, vis: &Visibility) -> Result<AsXmlParts> {
974 let target_ty_ident = &self.target_ty_ident;
975 let item_iter_ty_ident = &self.item_iter_ty_ident;
976 let item_iter_ty_lifetime = Lifetime {
977 apostrophe: Span::call_site(),
978 ident: Ident::new("xso_proc_as_xml_iter_lifetime", Span::call_site()),
979 };
980 let item_iter_ty = Type::Path(TypePath {
981 qself: None,
982 path: Path {
983 leading_colon: None,
984 segments: [PathSegment {
985 ident: item_iter_ty_ident.clone(),
986 arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
987 colon2_token: None,
988 lt_token: token::Lt {
989 spans: [Span::call_site()],
990 },
991 args: [GenericArgument::Lifetime(item_iter_ty_lifetime.clone())]
992 .into_iter()
993 .collect(),
994 gt_token: token::Gt {
995 spans: [Span::call_site()],
996 },
997 }),
998 }]
999 .into_iter()
1000 .collect(),
1001 },
1002 });
1003 let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident);
1004
1005 let defs = self
1006 .inner
1007 .make_as_item_iter_statemachine(
1008 target_ty_ident,
1009 &state_ty_ident,
1010 &item_iter_ty_lifetime,
1011 )?
1012 .render(
1013 vis,
1014 &ref_ty(
1015 ty_from_ident(target_ty_ident.clone()).into(),
1016 item_iter_ty_lifetime.clone(),
1017 ),
1018 &state_ty_ident,
1019 &item_iter_ty_lifetime,
1020 &item_iter_ty,
1021 )?;
1022
1023 Ok(AsXmlParts {
1024 defs,
1025 as_xml_iter_body: quote! {
1026 #item_iter_ty_ident::new(self)
1027 },
1028 item_iter_ty,
1029 item_iter_ty_lifetime,
1030 })
1031 }
1032
1033 fn debug(&self) -> bool {
1034 self.debug
1035 }
1036}