1use proc_macro2::{Span, TokenStream};
10use quote::{quote, ToTokens};
11use syn::{spanned::Spanned, *};
12
13use crate::error_message::ParentRef;
14use crate::field::{FieldBuilderPart, FieldDef, FieldIteratorPart, FieldTempInit, NestedMatcher};
15use crate::meta::{DiscardSpec, Flag, NameRef, NamespaceRef, QNameRef};
16use crate::scope::{mangle_member, AsItemsScope, FromEventsScope};
17use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State};
18use crate::types::{
19 default_fn, discard_builder_ty, feed_fn, namespace_ty, ncnamestr_cow_ty, phantom_lifetime_ty,
20 ref_ty, unknown_attribute_policy_path, unknown_child_policy_path,
21};
22
23fn resolve_policy(policy: Option<Ident>, mut enum_ref: Path) -> Expr {
24 match policy {
25 Some(ident) => {
26 enum_ref.segments.push(ident.into());
27 Expr::Path(ExprPath {
28 attrs: Vec::new(),
29 qself: None,
30 path: enum_ref,
31 })
32 }
33 None => {
34 let default_fn = default_fn(Type::Path(TypePath {
35 qself: None,
36 path: enum_ref,
37 }));
38 Expr::Call(ExprCall {
39 attrs: Vec::new(),
40 func: Box::new(default_fn),
41 paren_token: token::Paren::default(),
42 args: punctuated::Punctuated::new(),
43 })
44 }
45 }
46}
47
48pub(crate) struct Compound {
50 fields: Vec<FieldDef>,
52
53 unknown_attribute_policy: Expr,
55
56 unknown_child_policy: Expr,
58
59 discard_attr: Vec<(Option<NamespaceRef>, NameRef)>,
61
62 discard_text: Flag,
64}
65
66impl Compound {
67 pub(crate) fn from_field_defs<I: IntoIterator<Item = Result<FieldDef>>>(
69 compound_fields: I,
70 unknown_attribute_policy: Option<Ident>,
71 unknown_child_policy: Option<Ident>,
72 discard: Vec<DiscardSpec>,
73 ) -> Result<Self> {
74 let unknown_attribute_policy = resolve_policy(
75 unknown_attribute_policy,
76 unknown_attribute_policy_path(Span::call_site()),
77 );
78 let unknown_child_policy = resolve_policy(
79 unknown_child_policy,
80 unknown_child_policy_path(Span::call_site()),
81 );
82 let compound_fields = compound_fields.into_iter();
83 let size_hint = compound_fields.size_hint();
84 let mut fields = Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0));
85 let mut text_field = None;
86 for field in compound_fields {
87 let field = field?;
88
89 if field.is_text_field() {
90 if let Some(other_field) = text_field.as_ref() {
91 let mut err = Error::new_spanned(
92 field.member(),
93 "only one `#[xml(text)]` field allowed per compound",
94 );
95 err.combine(Error::new(
96 *other_field,
97 "the other `#[xml(text)]` field is here",
98 ));
99 return Err(err);
100 }
101 text_field = Some(field.member().span())
102 }
103
104 fields.push(field);
105 }
106
107 let mut discard_text = Flag::Absent;
108 let mut discard_attr = Vec::new();
109 for spec in discard {
110 match spec {
111 DiscardSpec::Text { span } => {
112 if let Some(field) = text_field.as_ref() {
113 let mut err = Error::new(
114 *field,
115 "cannot combine `#[xml(text)]` field with `discard(text)`",
116 );
117 err.combine(Error::new(
118 spec.span(),
119 "the discard(text) attribute is here",
120 ));
121 return Err(err);
122 }
123 if let Flag::Present(other) = discard_text {
124 let mut err = Error::new(
125 span,
126 "only one `discard(text)` meta is allowed per compound",
127 );
128 err.combine(Error::new(other, "the discard(text) meta is here"));
129 return Err(err);
130 }
131
132 discard_text = Flag::Present(span);
133 }
134
135 DiscardSpec::Attribute {
136 qname: QNameRef { namespace, name },
137 span,
138 } => {
139 let xml_namespace = namespace;
140 let xml_name = match name {
141 Some(v) => v,
142 None => {
143 return Err(Error::new(
144 span,
145 "discard(attribute) must specify a name, e.g. via discard(attribute = \"some-name\")",
146 ));
147 }
148 };
149 discard_attr.push((xml_namespace, xml_name));
150 }
151 }
152 }
153
154 Ok(Self {
155 fields,
156 unknown_attribute_policy,
157 unknown_child_policy,
158 discard_attr,
159 discard_text,
160 })
161 }
162
163 pub(crate) fn from_fields(
165 compound_fields: &Fields,
166 container_namespace: &NamespaceRef,
167 unknown_attribute_policy: Option<Ident>,
168 unknown_child_policy: Option<Ident>,
169 discard: Vec<DiscardSpec>,
170 ) -> Result<Self> {
171 Self::from_field_defs(
172 compound_fields.iter().enumerate().map(|(i, field)| {
173 let index = match i.try_into() {
174 Ok(v) => v,
175 Err(_) => {
178 return Err(Error::new_spanned(
179 field,
180 "okay, mate, that are way too many fields. get your life together.",
181 ))
182 }
183 };
184 FieldDef::from_field(field, index, container_namespace)
185 }),
186 unknown_attribute_policy,
187 unknown_child_policy,
188 discard,
189 )
190 }
191
192 pub(crate) fn make_from_events_statemachine(
198 &self,
199 state_ty_ident: &Ident,
200 output_name: &ParentRef,
201 state_prefix: &str,
202 ) -> Result<FromEventsSubmachine> {
203 let scope = FromEventsScope::new(state_ty_ident.clone());
204 let FromEventsScope {
205 ref attrs,
206 ref builder_data_ident,
207 ref text,
208 ref substate_data,
209 ref substate_result,
210 ..
211 } = scope;
212
213 let default_state_ident = quote::format_ident!("{}Default", state_prefix);
214 let discard_state_ident = quote::format_ident!("{}Discard", state_prefix);
215 let builder_data_ty: Type = TypePath {
216 qself: None,
217 path: quote::format_ident!("{}Data{}", state_ty_ident, state_prefix).into(),
218 }
219 .into();
220 let mut states = Vec::new();
221
222 let mut builder_data_def = TokenStream::default();
223 let mut builder_data_init = TokenStream::default();
224 let mut output_cons = TokenStream::default();
225 let mut child_matchers = TokenStream::default();
226 let mut fallback_child_matcher = None;
227 let mut text_handler = if self.discard_text.is_set() {
228 Some(quote! {
229 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
230 Self::#default_state_ident { #builder_data_ident }
231 ))
232 })
233 } else {
234 None
235 };
236 let mut extra_defs = TokenStream::default();
237 let is_tuple = !output_name.is_path();
238
239 for (i, field) in self.fields.iter().enumerate() {
240 let member = field.member();
241 let builder_field_name = mangle_member(member);
242 let part = field.make_builder_part(&scope, output_name)?;
243 let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
244
245 match part {
246 FieldBuilderPart::Init {
247 value: FieldTempInit { ty, init },
248 } => {
249 builder_data_def.extend(quote! {
250 #builder_field_name: #ty,
251 });
252
253 builder_data_init.extend(quote! {
254 #builder_field_name: #init,
255 });
256
257 if is_tuple {
258 output_cons.extend(quote! {
259 #builder_data_ident.#builder_field_name,
260 });
261 } else {
262 output_cons.extend(quote! {
263 #member: #builder_data_ident.#builder_field_name,
264 });
265 }
266 }
267
268 FieldBuilderPart::Text {
269 value: FieldTempInit { ty, init },
270 collect,
271 finalize,
272 } => {
273 if text_handler.is_some() {
274 panic!("more than one field attempts to collect text data");
277 }
278
279 builder_data_def.extend(quote! {
280 #builder_field_name: #ty,
281 });
282 builder_data_init.extend(quote! {
283 #builder_field_name: #init,
284 });
285 text_handler = Some(quote! {
286 #collect
287 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
288 Self::#default_state_ident { #builder_data_ident }
289 ))
290 });
291
292 if is_tuple {
293 output_cons.extend(quote! {
294 #finalize,
295 });
296 } else {
297 output_cons.extend(quote! {
298 #member: #finalize,
299 });
300 }
301 }
302
303 FieldBuilderPart::Nested {
304 extra_defs: field_extra_defs,
305 value: FieldTempInit { ty, init },
306 matcher,
307 builder,
308 collect,
309 finalize,
310 } => {
311 let feed = feed_fn(builder.clone());
312
313 states.push(State::new_with_builder(
314 state_name.clone(),
315 &builder_data_ident,
316 &builder_data_ty,
317 ).with_field(
318 substate_data,
319 &builder,
320 ).with_mut(substate_data).with_impl(quote! {
321 match #feed(&mut #substate_data, ev)? {
322 ::core::option::Option::Some(#substate_result) => {
323 #collect
324 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
325 #builder_data_ident,
326 }))
327 }
328 ::core::option::Option::None => {
329 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
330 #builder_data_ident,
331 #substate_data,
332 }))
333 }
334 }
335 }));
336
337 builder_data_def.extend(quote! {
338 #builder_field_name: #ty,
339 });
340
341 builder_data_init.extend(quote! {
342 #builder_field_name: #init,
343 });
344
345 match matcher {
346 NestedMatcher::Selective(matcher) => {
347 child_matchers.extend(quote! {
348 let (name, attrs) = match #matcher {
349 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs }) => (name, attrs),
350 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(e)) => return ::core::result::Result::Err(e),
351 ::core::result::Result::Ok(#substate_data) => {
352 return ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
353 #builder_data_ident,
354 #substate_data,
355 }))
356 }
357 };
358 });
359 }
360 NestedMatcher::Fallback(matcher) => {
361 if let Some((span, _)) = fallback_child_matcher.as_ref() {
362 let mut err = Error::new(
363 field.span(),
364 "more than one field is attempting to consume all unmatched child elements"
365 );
366 err.combine(Error::new(
367 *span,
368 "the previous field collecting all unmatched child elements is here"
369 ));
370 return Err(err);
371 }
372
373 let matcher = quote! {
374 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
375 #builder_data_ident,
376 #substate_data: { #matcher },
377 }))
378 };
379
380 fallback_child_matcher = Some((field.span(), matcher));
381 }
382 }
383
384 if is_tuple {
385 output_cons.extend(quote! {
386 #finalize,
387 });
388 } else {
389 output_cons.extend(quote! {
390 #member: #finalize,
391 });
392 }
393
394 extra_defs.extend(field_extra_defs);
395 }
396 }
397 }
398
399 let mut discard_attr = TokenStream::default();
400 for (xml_namespace, xml_name) in self.discard_attr.iter() {
401 let xml_namespace = match xml_namespace {
402 Some(v) => v.to_token_stream(),
403 None => quote! {
404 ::xso::exports::rxml::Namespace::none()
405 },
406 };
407 discard_attr.extend(quote! {
408 let _ = #attrs.remove(#xml_namespace, #xml_name);
409 });
410 }
411
412 let text_handler = match text_handler {
413 Some(v) => v,
414 None => quote! {
415 if !::xso::is_xml_whitespace(#text.as_bytes()) {
418 ::core::result::Result::Err(::xso::error::Error::Other("Unexpected text content".into()))
419 } else {
420 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
421 Self::#default_state_ident { #builder_data_ident }
422 ))
423 }
424 },
425 };
426
427 let unknown_attr_err = format!("Unknown attribute in {}.", output_name);
428 let unknown_child_err = format!("Unknown child in {}.", output_name);
429 let unknown_child_policy = &self.unknown_child_policy;
430
431 let output_cons = match output_name {
432 ParentRef::Named(ref path) => {
433 quote! {
434 #path { #output_cons }
435 }
436 }
437 ParentRef::Unnamed { .. } => {
438 quote! {
439 ( #output_cons )
440 }
441 }
442 };
443
444 let discard_builder_ty = discard_builder_ty(Span::call_site());
445 let discard_feed = feed_fn(discard_builder_ty.clone());
446 let child_fallback = match fallback_child_matcher {
447 Some((_, matcher)) => matcher,
448 None => quote! {
449 let _ = (name, attrs);
450 let _: () = #unknown_child_policy.apply_policy(#unknown_child_err)?;
451 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
452 #builder_data_ident,
453 #substate_data: #discard_builder_ty::new(),
454 }))
455 },
456 };
457
458 states.push(State::new_with_builder(
459 discard_state_ident.clone(),
460 &builder_data_ident,
461 &builder_data_ty,
462 ).with_field(
463 substate_data,
464 &discard_builder_ty,
465 ).with_mut(substate_data).with_impl(quote! {
466 match #discard_feed(&mut #substate_data, ev)? {
467 ::core::option::Option::Some(#substate_result) => {
468 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
469 #builder_data_ident,
470 }))
471 }
472 ::core::option::Option::None => {
473 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
474 #builder_data_ident,
475 #substate_data,
476 }))
477 }
478 }
479 }));
480
481 states.push(State::new_with_builder(
482 default_state_ident.clone(),
483 builder_data_ident,
484 &builder_data_ty,
485 ).with_impl(quote! {
486 match ev {
487 ::xso::exports::rxml::Event::EndElement(_) => {
489 ::core::result::Result::Ok(::core::ops::ControlFlow::Continue(
490 #output_cons
491 ))
492 }
493 ::xso::exports::rxml::Event::StartElement(_, name, attrs) => {
494 #child_matchers
495 #child_fallback
496 }
497 ::xso::exports::rxml::Event::Text(_, #text) => {
498 #text_handler
499 }
500 ::xso::exports::rxml::Event::XmlDeclaration(_, ::xso::exports::rxml::XmlVersion::V1_0) => ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
505 Self::#default_state_ident { #builder_data_ident }
506 ))
507 }
508 }));
509
510 let unknown_attribute_policy = &self.unknown_attribute_policy;
511
512 Ok(FromEventsSubmachine {
513 defs: quote! {
514 #extra_defs
515
516 struct #builder_data_ty {
517 #builder_data_def
518 }
519 },
520 states,
521 init: quote! {
522 let #builder_data_ident = #builder_data_ty {
523 #builder_data_init
524 };
525 #discard_attr
526 if #attrs.len() > 0 {
527 let _: () = #unknown_attribute_policy.apply_policy(#unknown_attr_err)?;
528 }
529 ::core::result::Result::Ok(#state_ty_ident::#default_state_ident { #builder_data_ident })
530 },
531 })
532 }
533
534 pub(crate) fn make_as_item_iter_statemachine(
544 &self,
545 input_name: &ParentRef,
546 state_ty_ident: &Ident,
547 state_prefix: &str,
548 lifetime: &Lifetime,
549 ) -> Result<AsItemsSubmachine> {
550 let scope = AsItemsScope::new(lifetime, state_ty_ident.clone());
551
552 let element_head_start_state_ident =
553 quote::format_ident!("{}ElementHeadStart", state_prefix);
554 let element_head_end_state_ident = quote::format_ident!("{}ElementHeadEnd", state_prefix);
555 let element_foot_state_ident = quote::format_ident!("{}ElementFoot", state_prefix);
556 let name_ident = quote::format_ident!("name");
557 let ns_ident = quote::format_ident!("ns");
558 let dummy_ident = quote::format_ident!("dummy");
559 let mut states = Vec::new();
560
561 let is_tuple = !input_name.is_path();
562 let mut destructure = TokenStream::default();
563 let mut start_init = TokenStream::default();
564 let mut extra_defs = TokenStream::default();
565
566 states.push(
567 State::new(element_head_start_state_ident.clone())
568 .with_field(&dummy_ident, &phantom_lifetime_ty(lifetime.clone()))
569 .with_field(&ns_ident, &namespace_ty(Span::call_site()))
570 .with_field(
571 &name_ident,
572 &ncnamestr_cow_ty(Span::call_site(), lifetime.clone()),
573 ),
574 );
575
576 let mut element_head_end_idx = states.len();
577 states.push(
578 State::new(element_head_end_state_ident.clone()).with_impl(quote! {
579 ::core::option::Option::Some(::xso::Item::ElementHeadEnd)
580 }),
581 );
582
583 for (i, field) in self.fields.iter().enumerate() {
584 let member = field.member();
585 let bound_name = mangle_member(member);
586 let part = field.make_iterator_part(&scope, input_name, &bound_name)?;
587 let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
588 let ty = scope.borrow(field.ty().clone());
589
590 match part {
591 FieldIteratorPart::Header { generator } => {
592 for state in &mut states[..element_head_end_idx] {
595 state.add_field(&bound_name, &ty);
596 }
597 states.insert(
598 element_head_end_idx,
599 State::new(state_name)
600 .with_field(&bound_name, &ty)
601 .with_impl(quote! {
602 #generator
603 }),
604 );
605 element_head_end_idx += 1;
606
607 if is_tuple {
608 destructure.extend(quote! {
609 ref #bound_name,
610 });
611 } else {
612 destructure.extend(quote! {
613 #member: ref #bound_name,
614 });
615 }
616 start_init.extend(quote! {
617 #bound_name,
618 });
619 }
620
621 FieldIteratorPart::Text { generator } => {
622 for state in states.iter_mut() {
625 state.add_field(&bound_name, &ty);
626 }
627 states.push(
628 State::new(state_name)
629 .with_field(&bound_name, &ty)
630 .with_impl(quote! {
631 #generator.map(|value| ::xso::Item::Text(
632 value,
633 ))
634 }),
635 );
636 if is_tuple {
637 destructure.extend(quote! {
638 #bound_name,
639 });
640 } else {
641 destructure.extend(quote! {
642 #member: #bound_name,
643 });
644 }
645 start_init.extend(quote! {
646 #bound_name,
647 });
648 }
649
650 FieldIteratorPart::Content {
651 extra_defs: field_extra_defs,
652 value: FieldTempInit { ty, init },
653 generator,
654 } => {
655 for state in states.iter_mut() {
658 state.add_field(&bound_name, &ty);
659 }
660
661 states.push(
662 State::new(state_name.clone())
663 .with_field(&bound_name, &ty)
664 .with_mut(&bound_name)
665 .with_impl(quote! {
666 #generator?
667 }),
668 );
669 if is_tuple {
670 destructure.extend(quote! {
671 #bound_name,
672 });
673 } else {
674 destructure.extend(quote! {
675 #member: #bound_name,
676 });
677 }
678 start_init.extend(quote! {
679 #bound_name: #init,
680 });
681
682 extra_defs.extend(field_extra_defs);
683 }
684 }
685 }
686
687 states[0].set_impl(quote! {
688 {
689 ::core::option::Option::Some(::xso::Item::ElementHeadStart(
690 #ns_ident,
691 #name_ident,
692 ))
693 }
694 });
695
696 states.push(
697 State::new(element_foot_state_ident.clone()).with_impl(quote! {
698 ::core::option::Option::Some(::xso::Item::ElementFoot)
699 }),
700 );
701
702 let destructure = match input_name {
703 ParentRef::Named(ref input_path) => quote! {
704 #input_path { #destructure }
705 },
706 ParentRef::Unnamed { .. } => quote! {
707 ( #destructure )
708 },
709 };
710
711 Ok(AsItemsSubmachine {
712 defs: extra_defs,
713 states,
714 destructure,
715 init: quote! {
716 Self::#element_head_start_state_ident { #dummy_ident: ::core::marker::PhantomData, #name_ident: name.1, #ns_ident: name.0, #start_init }
717 },
718 })
719 }
720
721 pub(crate) fn single_ty(&self) -> Option<&Type> {
726 if self.fields.len() > 1 {
727 return None;
728 }
729 self.fields.get(0).map(|x| x.ty())
730 }
731
732 pub(crate) fn to_tuple_ty(&self) -> TypeTuple {
735 TypeTuple {
736 paren_token: token::Paren::default(),
737 elems: self.fields.iter().map(|x| x.ty().clone()).collect(),
738 }
739 }
740
741 pub(crate) fn to_single_or_tuple_ty(&self) -> Type {
744 match self.single_ty() {
745 None => self.to_tuple_ty().into(),
746 Some(v) => v.clone(),
747 }
748 }
749
750 pub(crate) fn to_ref_tuple_ty(&self, lifetime: &Lifetime) -> TypeTuple {
754 TypeTuple {
755 paren_token: token::Paren::default(),
756 elems: self
757 .fields
758 .iter()
759 .map(|x| ref_ty(x.ty().clone(), lifetime.clone()))
760 .collect(),
761 }
762 }
763
764 pub(crate) fn field_count(&self) -> usize {
766 self.fields.len()
767 }
768}