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