1use proc_macro2::{Span, TokenStream};
10use quote::{quote, quote_spanned, ToTokens};
11use syn::{spanned::Spanned, *};
12
13use std::collections::{hash_map::Entry, HashMap};
14
15use crate::error_message::{FieldName, ParentRef};
16use crate::field::{FieldBuilderPart, FieldDef, FieldIteratorPart, FieldTempInit, NestedMatcher};
17use crate::meta::{DiscardSpec, Flag, NameRef, NamespaceRef, QNameRef};
18use crate::scope::{mangle_member, AsItemsScope, FromEventsScope};
19use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State};
20use crate::types::{
21 default_fn, discard_builder_ty, feed_fn, namespace_ty, ncnamestr_cow_ty, phantom_lifetime_ty,
22 ref_ty, unknown_attribute_policy_path, unknown_child_policy_path,
23};
24
25fn resolve_policy(policy: Option<Ident>, mut enum_ref: Path) -> Expr {
26 match policy {
27 Some(ident) => {
28 enum_ref.segments.push(ident.into());
29 Expr::Path(ExprPath {
30 attrs: Vec::new(),
31 qself: None,
32 path: enum_ref,
33 })
34 }
35 None => {
36 let default_fn = default_fn(Type::Path(TypePath {
37 qself: None,
38 path: enum_ref,
39 }));
40 Expr::Call(ExprCall {
41 attrs: Vec::new(),
42 func: Box::new(default_fn),
43 paren_token: token::Paren::default(),
44 args: punctuated::Punctuated::new(),
45 })
46 }
47 }
48}
49
50pub(crate) struct Compound {
52 fields: Vec<FieldDef>,
54
55 unknown_attribute_policy: Expr,
57
58 unknown_child_policy: Expr,
60
61 discard_attr: Vec<(Option<NamespaceRef>, NameRef)>,
63
64 discard_text: Flag,
66
67 selected_attributes: Vec<(QNameRef, Member)>,
72}
73
74impl Compound {
75 pub(crate) fn from_field_defs<I: IntoIterator<Item = Result<FieldDef>>>(
77 compound_fields: I,
78 unknown_attribute_policy: Option<Ident>,
79 unknown_child_policy: Option<Ident>,
80 discard: Vec<DiscardSpec>,
81 ) -> Result<Self> {
82 let unknown_attribute_policy = resolve_policy(
83 unknown_attribute_policy,
84 unknown_attribute_policy_path(Span::call_site()),
85 );
86 let unknown_child_policy = resolve_policy(
87 unknown_child_policy,
88 unknown_child_policy_path(Span::call_site()),
89 );
90 let compound_fields = compound_fields.into_iter();
91 let size_hint = compound_fields.size_hint();
92 let mut fields = Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0));
93 let mut text_field = None;
94 let mut selected_attributes: HashMap<QNameRef, Member> = HashMap::new();
95 for field in compound_fields {
96 let field = field?;
97
98 if field.is_text_field() {
99 if let Some(other_field) = text_field.as_ref() {
100 let mut err = Error::new_spanned(
101 field.member(),
102 "only one `#[xml(text)]` field allowed per compound",
103 );
104 err.combine(Error::new(
105 *other_field,
106 "the other `#[xml(text)]` field is here",
107 ));
108 return Err(err);
109 }
110 text_field = Some(field.member().span())
111 }
112
113 if let Some(qname) = field.captures_attribute() {
114 let span = field.span();
115 match selected_attributes.entry(qname) {
116 Entry::Occupied(o) => {
117 let mut err = Error::new(
118 span,
119 "this field XML field matches the same attribute as another field",
120 );
121 err.combine(Error::new(
122 o.get().span(),
123 "the other field matching the same attribute is here",
124 ));
125 return Err(err);
126 }
127 Entry::Vacant(v) => {
128 v.insert(field.member().clone());
129 }
130 }
131 }
132
133 fields.push(field);
134 }
135
136 let mut discard_text = Flag::Absent;
137 let mut discard_attr = Vec::new();
138 for spec in discard {
139 match spec {
140 DiscardSpec::Text { span } => {
141 if let Some(field) = text_field.as_ref() {
142 let mut err = Error::new(
143 *field,
144 "cannot combine `#[xml(text)]` field with `discard(text)`",
145 );
146 err.combine(Error::new(
147 spec.span(),
148 "the discard(text) attribute is here",
149 ));
150 return Err(err);
151 }
152 if let Flag::Present(other) = discard_text {
153 let mut err = Error::new(
154 span,
155 "only one `discard(text)` meta is allowed per compound",
156 );
157 err.combine(Error::new(other, "the discard(text) meta is here"));
158 return Err(err);
159 }
160
161 discard_text = Flag::Present(span);
162 }
163
164 DiscardSpec::Attribute {
165 qname: QNameRef { namespace, name },
166 span,
167 } => {
168 let xml_namespace = namespace;
169 let xml_name = match name {
170 Some(v) => v,
171 None => {
172 return Err(Error::new(
173 span,
174 "discard(attribute) must specify a name, e.g. via discard(attribute = \"some-name\")",
175 ));
176 }
177 };
178 discard_attr.push((xml_namespace, xml_name));
179 }
180 }
181 }
182
183 Ok(Self {
184 fields,
185 unknown_attribute_policy,
186 unknown_child_policy,
187 discard_attr,
188 discard_text,
189 selected_attributes: selected_attributes.into_iter().collect(),
190 })
191 }
192
193 pub(crate) fn from_fields(
195 compound_fields: &Fields,
196 container_namespace: &NamespaceRef,
197 unknown_attribute_policy: Option<Ident>,
198 unknown_child_policy: Option<Ident>,
199 discard: Vec<DiscardSpec>,
200 ) -> Result<Self> {
201 Self::from_field_defs(
202 compound_fields.iter().enumerate().map(|(i, field)| {
203 let index = match i.try_into() {
204 Ok(v) => v,
205 Err(_) => {
208 return Err(Error::new_spanned(
209 field,
210 "okay, mate, that are way too many fields. get your life together.",
211 ))
212 }
213 };
214 FieldDef::from_field(field, index, container_namespace)
215 }),
216 unknown_attribute_policy,
217 unknown_child_policy,
218 discard,
219 )
220 }
221
222 fn assert_disjunct_attributes(&self) -> TokenStream {
227 let mut checks = TokenStream::default();
228
229 for (i, (qname_a, member_a)) in self.selected_attributes.iter().enumerate() {
234 for (j, (qname_b, member_b)) in self.selected_attributes.iter().enumerate() {
235 if i == j {
236 continue;
237 }
238 let span = member_a.span();
244 let (member_a, member_b) = if i > j {
245 (member_b, member_a)
246 } else {
247 (member_a, member_b)
248 };
249 if qname_a.namespace.is_some() != qname_b.namespace.is_some() {
250 continue;
252 }
253 let Some((name_a, name_b)) = qname_a.name.as_ref().zip(qname_b.name.as_ref())
254 else {
255 panic!("selected attribute has no XML local name");
256 };
257
258 let mut check = quote! {
259 ::xso::exports::const_str_eq(#name_a.as_str(), #name_b.as_str())
260 };
261
262 let namespaces = qname_a.namespace.as_ref().zip(qname_b.namespace.as_ref());
263 if let Some((ns_a, ns_b)) = namespaces {
264 check.extend(quote! {
265 && ::xso::exports::const_str_eq(#ns_a, #ns_b)
266 });
267 };
268
269 let attr_a = if let Some(namespace_a) = qname_a.namespace.as_ref() {
270 format!("{{{}}}{}", namespace_a, name_a)
271 } else {
272 format!("{}", name_a)
273 };
274
275 let attr_b = if let Some(namespace_b) = qname_b.namespace.as_ref() {
276 format!("{{{}}}{}", namespace_b, name_b)
277 } else {
278 format!("{}", name_b)
279 };
280
281 let attr_a = attr_a.replace('{', "{{").replace('}', "}}");
289 let attr_b = attr_b.replace('{', "{{").replace('}', "}}");
290 let field_a = FieldName(&member_a)
291 .to_string()
292 .replace('{', "{{")
293 .replace('}', "}}");
294 let field_b = FieldName(&member_b)
295 .to_string()
296 .replace('{', "{{")
297 .replace('}', "}}");
298
299 checks.extend(quote_spanned! {span=>
303 const _: () = { if #check {
304 panic!(concat!("member ", #field_a, " and member ", #field_b, " match the same XML attribute: ", #attr_a, " == ", #attr_b));
305 } };
306 })
307 }
308 }
309
310 checks
311 }
312
313 pub(crate) fn make_from_events_statemachine(
319 &self,
320 state_ty_ident: &Ident,
321 output_name: &ParentRef,
322 state_prefix: &str,
323 ) -> Result<FromEventsSubmachine> {
324 let scope = FromEventsScope::new(state_ty_ident.clone());
325 let FromEventsScope {
326 ref attrs,
327 ref builder_data_ident,
328 ref text,
329 ref substate_data,
330 ref substate_result,
331 ..
332 } = scope;
333
334 let default_state_ident = quote::format_ident!("{}Default", state_prefix);
335 let discard_state_ident = quote::format_ident!("{}Discard", state_prefix);
336 let builder_data_ty: Type = TypePath {
337 qself: None,
338 path: quote::format_ident!("{}Data{}", state_ty_ident, state_prefix).into(),
339 }
340 .into();
341 let mut states = Vec::new();
342
343 let mut builder_data_def = TokenStream::default();
344 let mut builder_data_init = TokenStream::default();
345 let mut output_cons = TokenStream::default();
346 let mut child_matchers = TokenStream::default();
347 let mut fallback_child_matcher = None;
348 let mut text_handler = if self.discard_text.is_set() {
349 Some(quote! {
350 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
351 Self::#default_state_ident { #builder_data_ident }
352 ))
353 })
354 } else {
355 None
356 };
357 let mut extra_defs = TokenStream::default();
358 let is_tuple = !output_name.is_path();
359
360 for (i, field) in self.fields.iter().enumerate() {
361 let member = field.member();
362 let builder_field_name = mangle_member(member);
363 let part = field.make_builder_part(&scope, output_name)?;
364 let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
365
366 match part {
367 FieldBuilderPart::Init {
368 value: FieldTempInit { ty, init },
369 } => {
370 builder_data_def.extend(quote! {
371 #builder_field_name: #ty,
372 });
373
374 builder_data_init.extend(quote! {
375 #builder_field_name: #init,
376 });
377
378 if is_tuple {
379 output_cons.extend(quote! {
380 #builder_data_ident.#builder_field_name,
381 });
382 } else {
383 output_cons.extend(quote! {
384 #member: #builder_data_ident.#builder_field_name,
385 });
386 }
387 }
388
389 FieldBuilderPart::Text {
390 value: FieldTempInit { ty, init },
391 collect,
392 finalize,
393 } => {
394 if text_handler.is_some() {
395 panic!("more than one field attempts to collect text data");
398 }
399
400 builder_data_def.extend(quote! {
401 #builder_field_name: #ty,
402 });
403 builder_data_init.extend(quote! {
404 #builder_field_name: #init,
405 });
406 text_handler = Some(quote! {
407 #collect
408 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
409 Self::#default_state_ident { #builder_data_ident }
410 ))
411 });
412
413 if is_tuple {
414 output_cons.extend(quote! {
415 #finalize,
416 });
417 } else {
418 output_cons.extend(quote! {
419 #member: #finalize,
420 });
421 }
422 }
423
424 FieldBuilderPart::Nested {
425 extra_defs: field_extra_defs,
426 value: FieldTempInit { ty, init },
427 matcher,
428 builder,
429 collect,
430 finalize,
431 } => {
432 let feed = feed_fn(builder.clone());
433
434 let mut substate_data_ident = substate_data.clone();
435 substate_data_ident.set_span(ty.span());
436 states.push(State::new_with_builder(
437 state_name.clone(),
438 builder_data_ident,
439 &builder_data_ty,
440 ).with_field(
441 &substate_data_ident,
442 &builder,
443 ).with_mut(substate_data).with_impl(quote! {
444 match #feed(&mut #substate_data, ev, ctx)? {
445 ::core::option::Option::Some(#substate_result) => {
446 #collect
447 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
448 #builder_data_ident,
449 }))
450 }
451 ::core::option::Option::None => {
452 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
453 #builder_data_ident,
454 #substate_data,
455 }))
456 }
457 }
458 }));
459
460 builder_data_def.extend(quote! {
461 #builder_field_name: #ty,
462 });
463
464 builder_data_init.extend(quote! {
465 #builder_field_name: #init,
466 });
467
468 match matcher {
469 NestedMatcher::Selective(matcher) => {
470 child_matchers.extend(quote! {
471 let (name, attrs) = match #matcher {
472 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs }) => (name, attrs),
473 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(e)) => return ::core::result::Result::Err(e),
474 ::core::result::Result::Ok(#substate_data) => {
475 return ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
476 #builder_data_ident,
477 #substate_data,
478 }))
479 }
480 };
481 });
482 }
483 NestedMatcher::Fallback(matcher) => {
484 if let Some((span, _)) = fallback_child_matcher.as_ref() {
485 let mut err = Error::new(
486 field.span(),
487 "more than one field is attempting to consume all unmatched child elements"
488 );
489 err.combine(Error::new(
490 *span,
491 "the previous field collecting all unmatched child elements is here"
492 ));
493 return Err(err);
494 }
495
496 let matcher = quote! {
497 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
498 #builder_data_ident,
499 #substate_data: { #matcher },
500 }))
501 };
502
503 fallback_child_matcher = Some((field.span(), matcher));
504 }
505 }
506
507 if is_tuple {
508 output_cons.extend(quote! {
509 #finalize,
510 });
511 } else {
512 output_cons.extend(quote! {
513 #member: #finalize,
514 });
515 }
516
517 extra_defs.extend(field_extra_defs);
518 }
519 }
520 }
521
522 let mut discard_attr = quote! {
525 let _ = #attrs.remove(::xso::exports::rxml::Namespace::xml(), "lang");
526 };
527 for (xml_namespace, xml_name) in self.discard_attr.iter() {
528 let xml_namespace = match xml_namespace {
529 Some(v) => v.to_token_stream(),
530 None => quote! {
531 ::xso::exports::rxml::Namespace::none()
532 },
533 };
534 discard_attr.extend(quote! {
535 let _ = #attrs.remove(#xml_namespace, #xml_name);
536 });
537 }
538
539 let text_handler = match text_handler {
540 Some(v) => v,
541 None => quote! {
542 if !::xso::is_xml_whitespace(#text.as_bytes()) {
545 ::core::result::Result::Err(::xso::error::Error::Other("Unexpected text content".into()))
546 } else {
547 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
548 Self::#default_state_ident { #builder_data_ident }
549 ))
550 }
551 },
552 };
553
554 let unknown_attr_err = format!("Unknown attribute in {}.", output_name);
555 let unknown_child_err = format!("Unknown child in {}.", output_name);
556 let unknown_child_policy = &self.unknown_child_policy;
557
558 let output_cons = match output_name {
559 ParentRef::Named(ref path) => {
560 quote! {
561 #path { #output_cons }
562 }
563 }
564 ParentRef::Unnamed { .. } => {
565 quote! {
566 ( #output_cons )
567 }
568 }
569 };
570
571 let discard_builder_ty = discard_builder_ty(Span::call_site());
572 let discard_feed = feed_fn(discard_builder_ty.clone());
573 let child_fallback = match fallback_child_matcher {
574 Some((_, matcher)) => matcher,
575 None => quote! {
576 let _ = (name, attrs);
577 let _: () = #unknown_child_policy.apply_policy(#unknown_child_err)?;
578 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
579 #builder_data_ident,
580 #substate_data: #discard_builder_ty::new(),
581 }))
582 },
583 };
584
585 states.push(State::new_with_builder(
586 discard_state_ident.clone(),
587 builder_data_ident,
588 &builder_data_ty,
589 ).with_field(
590 substate_data,
591 &discard_builder_ty,
592 ).with_mut(substate_data).with_impl(quote! {
593 match #discard_feed(&mut #substate_data, ev, ctx)? {
594 ::core::option::Option::Some(#substate_result) => {
595 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
596 #builder_data_ident,
597 }))
598 }
599 ::core::option::Option::None => {
600 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
601 #builder_data_ident,
602 #substate_data,
603 }))
604 }
605 }
606 }));
607
608 states.push(State::new_with_builder(
609 default_state_ident.clone(),
610 builder_data_ident,
611 &builder_data_ty,
612 ).with_impl(quote! {
613 match ev {
614 ::xso::exports::rxml::Event::EndElement(_) => {
616 ::core::result::Result::Ok(::core::ops::ControlFlow::Continue(
617 #output_cons
618 ))
619 }
620 ::xso::exports::rxml::Event::StartElement(_, name, attrs) => {
621 #child_matchers
622 #child_fallback
623 }
624 ::xso::exports::rxml::Event::Text(_, #text) => {
625 #text_handler
626 }
627 ::xso::exports::rxml::Event::XmlDeclaration(_, ::xso::exports::rxml::XmlVersion::V1_0) => ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
632 Self::#default_state_ident { #builder_data_ident }
633 ))
634 }
635 }));
636
637 let unknown_attribute_policy = &self.unknown_attribute_policy;
638
639 let checks = self.assert_disjunct_attributes();
640
641 Ok(FromEventsSubmachine {
642 defs: quote! {
643 #extra_defs
644 #checks
645
646 struct #builder_data_ty {
647 #builder_data_def
648 }
649 },
650 states,
651 init: quote! {
652 let #builder_data_ident = #builder_data_ty {
653 #builder_data_init
654 };
655 #discard_attr
656 if #attrs.len() > 0 {
657 let _: () = #unknown_attribute_policy.apply_policy(#unknown_attr_err)?;
658 }
659 ::core::result::Result::Ok(#state_ty_ident::#default_state_ident { #builder_data_ident })
660 },
661 })
662 }
663
664 pub(crate) fn make_as_item_iter_statemachine(
674 &self,
675 input_name: &ParentRef,
676 state_ty_ident: &Ident,
677 state_prefix: &str,
678 lifetime: &Lifetime,
679 ) -> Result<AsItemsSubmachine> {
680 let scope = AsItemsScope::new(lifetime, state_ty_ident.clone());
681
682 let element_head_start_state_ident =
683 quote::format_ident!("{}ElementHeadStart", state_prefix);
684 let element_head_end_state_ident = quote::format_ident!("{}ElementHeadEnd", state_prefix);
685 let element_foot_state_ident = quote::format_ident!("{}ElementFoot", state_prefix);
686 let name_ident = quote::format_ident!("name");
687 let ns_ident = quote::format_ident!("ns");
688 let dummy_ident = quote::format_ident!("dummy");
689 let mut header_states = Vec::new();
690 let mut body_states = Vec::new();
691
692 let is_tuple = !input_name.is_path();
693 let mut destructure = TokenStream::default();
694 let mut start_init = TokenStream::default();
695 let mut extra_defs = TokenStream::default();
696
697 header_states.push(
698 State::new(element_head_start_state_ident.clone())
699 .with_field(&dummy_ident, &phantom_lifetime_ty(lifetime.clone()))
700 .with_field(&ns_ident, &namespace_ty(Span::call_site()))
701 .with_field(
702 &name_ident,
703 &ncnamestr_cow_ty(Span::call_site(), lifetime.clone()),
704 ),
705 );
706
707 body_states.push((
708 None,
709 State::new(element_head_end_state_ident.clone()).with_impl(quote! {
710 ::core::option::Option::Some(::xso::Item::ElementHeadEnd)
711 }),
712 ));
713
714 for (i, field) in self.fields.iter().enumerate() {
715 let member = field.member();
716 let bound_name = mangle_member(member);
717 let part = field.make_iterator_part(&scope, input_name, &bound_name)?;
718 let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
719 let ty = scope.borrow(field.ty().clone());
720
721 match part {
722 FieldIteratorPart::Header { generator } => {
723 for state in header_states.iter_mut() {
731 state.add_field(&bound_name, &ty);
732 }
733 header_states.push(
734 State::new(state_name)
735 .with_field(&bound_name, &ty)
736 .with_impl(quote! {
737 #generator
738 }),
739 );
740
741 if is_tuple {
742 destructure.extend(quote! {
743 ref #bound_name,
744 });
745 } else {
746 destructure.extend(quote! {
747 #member: ref #bound_name,
748 });
749 }
750 start_init.extend(quote! {
751 #bound_name,
752 });
753 }
754
755 FieldIteratorPart::Text { generator } => {
756 for (_, state) in body_states.iter_mut() {
763 state.add_field(&bound_name, &ty);
764 }
765 let state = State::new(state_name)
766 .with_field(&bound_name, &ty)
767 .with_impl(quote! {
768 #generator.map(|value| ::xso::Item::Text(
769 value,
770 ))
771 });
772 if is_tuple {
773 destructure.extend(quote! {
774 #bound_name,
775 });
776 } else {
777 destructure.extend(quote! {
778 #member: #bound_name,
779 });
780 }
781 start_init.extend(quote! {
782 #bound_name,
783 });
784 body_states.push((Some((bound_name, ty)), state));
785 }
786
787 FieldIteratorPart::Content {
788 extra_defs: field_extra_defs,
789 value: FieldTempInit { ty, init },
790 generator,
791 } => {
792 for (_, state) in body_states.iter_mut() {
799 state.add_field(&bound_name, &ty);
800 }
801
802 let state = State::new(state_name.clone())
803 .with_field(&bound_name, &ty)
804 .with_mut(&bound_name)
805 .with_impl(quote! {
806 #generator?
807 });
808 if is_tuple {
809 destructure.extend(quote! {
810 #bound_name,
811 });
812 } else {
813 destructure.extend(quote! {
814 #member: #bound_name,
815 });
816 }
817 start_init.extend(quote! {
818 #bound_name: #init,
819 });
820
821 extra_defs.extend(field_extra_defs);
822 body_states.push((Some((bound_name, ty)), state));
823 }
824 }
825 }
826
827 header_states[0].set_impl(quote! {
828 {
829 ::core::option::Option::Some(::xso::Item::ElementHeadStart(
830 #ns_ident,
831 #name_ident,
832 ))
833 }
834 });
835
836 for (data, _) in body_states.iter() {
837 if let Some((bound_name, ty)) = data.as_ref() {
838 for state in header_states.iter_mut() {
839 state.add_field(bound_name, ty);
840 }
841 }
842 }
843
844 header_states.extend(body_states.into_iter().map(|(_, state)| state));
845 let mut states = header_states;
846
847 states.push(
848 State::new(element_foot_state_ident.clone()).with_impl(quote! {
849 ::core::option::Option::Some(::xso::Item::ElementFoot)
850 }),
851 );
852
853 let destructure = match input_name {
854 ParentRef::Named(ref input_path) => quote! {
855 #input_path { #destructure }
856 },
857 ParentRef::Unnamed { .. } => quote! {
858 ( #destructure )
859 },
860 };
861
862 let checks = self.assert_disjunct_attributes();
863 extra_defs.extend(checks);
864
865 Ok(AsItemsSubmachine {
866 defs: extra_defs,
867 states,
868 destructure,
869 init: quote! {
870 Self::#element_head_start_state_ident { #dummy_ident: ::core::marker::PhantomData, #name_ident: name.1, #ns_ident: name.0, #start_init }
871 },
872 })
873 }
874
875 pub(crate) fn single_ty(&self) -> Option<&Type> {
880 if self.fields.len() > 1 {
881 return None;
882 }
883 self.fields.first().map(|x| x.ty())
884 }
885
886 pub(crate) fn to_tuple_ty(&self) -> TypeTuple {
889 TypeTuple {
890 paren_token: token::Paren::default(),
891 elems: self.fields.iter().map(|x| x.ty().clone()).collect(),
892 }
893 }
894
895 pub(crate) fn to_single_or_tuple_ty(&self) -> Type {
898 match self.single_ty() {
899 None => self.to_tuple_ty().into(),
900 Some(v) => v.clone(),
901 }
902 }
903
904 pub(crate) fn to_ref_tuple_ty(&self, lifetime: &Lifetime) -> TypeTuple {
908 TypeTuple {
909 paren_token: token::Paren::default(),
910 elems: self
911 .fields
912 .iter()
913 .map(|x| ref_ty(x.ty().clone(), lifetime.clone()))
914 .collect(),
915 }
916 }
917
918 pub(crate) fn field_count(&self) -> usize {
920 self.fields.len()
921 }
922}