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::common::GenericsInfo;
16use crate::error_message::{FieldName, ParentRef};
17use crate::field::{FieldBuilderPart, FieldDef, FieldIteratorPart, FieldTempInit, NestedMatcher};
18use crate::meta::{DiscardSpec, Flag, NameRef, NamespaceRef, QNameRef};
19use crate::scope::{mangle_member, AsItemsScope, FromEventsScope};
20use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State};
21use crate::types::{
22 default_fn, discard_builder_ty, feed_fn, namespace_ty, ncnamestr_cow_ty, phantom_lifetime_ty,
23 ref_ty, unknown_attribute_policy_path, unknown_child_policy_path,
24};
25
26fn resolve_policy(policy: Option<Ident>, mut enum_ref: Path) -> Expr {
27 match policy {
28 Some(ident) => {
29 enum_ref.segments.push(ident.into());
30 Expr::Path(ExprPath {
31 attrs: Vec::new(),
32 qself: None,
33 path: enum_ref,
34 })
35 }
36 None => {
37 let default_fn = default_fn(Type::Path(TypePath {
38 qself: None,
39 path: enum_ref,
40 }));
41 Expr::Call(ExprCall {
42 attrs: Vec::new(),
43 func: Box::new(default_fn),
44 paren_token: token::Paren::default(),
45 args: punctuated::Punctuated::new(),
46 })
47 }
48 }
49}
50
51pub(crate) struct Compound {
53 fields: Vec<FieldDef>,
55
56 unknown_attribute_policy: Expr,
58
59 unknown_child_policy: Expr,
61
62 discard_attr: Vec<(Option<NamespaceRef>, NameRef)>,
64
65 discard_text: Flag,
67
68 selected_attributes: Vec<(QNameRef, Member)>,
73}
74
75impl Compound {
76 pub(crate) fn from_field_defs<I: IntoIterator<Item = Result<FieldDef>>>(
78 compound_fields: I,
79 unknown_attribute_policy: Option<Ident>,
80 unknown_child_policy: Option<Ident>,
81 discard: Vec<DiscardSpec>,
82 ) -> Result<Self> {
83 let unknown_attribute_policy = resolve_policy(
84 unknown_attribute_policy,
85 unknown_attribute_policy_path(Span::call_site()),
86 );
87 let unknown_child_policy = resolve_policy(
88 unknown_child_policy,
89 unknown_child_policy_path(Span::call_site()),
90 );
91 let compound_fields = compound_fields.into_iter();
92 let size_hint = compound_fields.size_hint();
93 let mut fields = Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0));
94 let mut text_field = None;
95 let mut selected_attributes: HashMap<QNameRef, Member> = HashMap::new();
96 for field in compound_fields {
97 let field = field?;
98
99 if field.is_text_field() {
100 if let Some(other_field) = text_field.as_ref() {
101 let mut err = Error::new_spanned(
102 field.member(),
103 "only one `#[xml(text)]` field allowed per compound",
104 );
105 err.combine(Error::new(
106 *other_field,
107 "the other `#[xml(text)]` field is here",
108 ));
109 return Err(err);
110 }
111 text_field = Some(field.member().span())
112 }
113
114 if let Some(qname) = field.captures_attribute() {
115 let span = field.span();
116 match selected_attributes.entry(qname) {
117 Entry::Occupied(o) => {
118 let mut err = Error::new(
119 span,
120 "this field XML field matches the same attribute as another field",
121 );
122 err.combine(Error::new(
123 o.get().span(),
124 "the other field matching the same attribute is here",
125 ));
126 return Err(err);
127 }
128 Entry::Vacant(v) => {
129 v.insert(field.member().clone());
130 }
131 }
132 }
133
134 fields.push(field);
135 }
136
137 let mut discard_text = Flag::Absent;
138 let mut discard_attr = Vec::new();
139 for spec in discard {
140 match spec {
141 DiscardSpec::Text { span } => {
142 if let Some(field) = text_field.as_ref() {
143 let mut err = Error::new(
144 *field,
145 "cannot combine `#[xml(text)]` field with `discard(text)`",
146 );
147 err.combine(Error::new(
148 spec.span(),
149 "the discard(text) attribute is here",
150 ));
151 return Err(err);
152 }
153 if let Flag::Present(other) = discard_text {
154 let mut err = Error::new(
155 span,
156 "only one `discard(text)` meta is allowed per compound",
157 );
158 err.combine(Error::new(other, "the discard(text) meta is here"));
159 return Err(err);
160 }
161
162 discard_text = Flag::Present(span);
163 }
164
165 DiscardSpec::Attribute {
166 qname: QNameRef { namespace, name },
167 span,
168 } => {
169 let xml_namespace = namespace;
170 let xml_name = match name {
171 Some(v) => v,
172 None => {
173 return Err(Error::new(
174 span,
175 "discard(attribute) must specify a name, e.g. via discard(attribute = \"some-name\")",
176 ));
177 }
178 };
179 discard_attr.push((xml_namespace, xml_name));
180 }
181 }
182 }
183
184 Ok(Self {
185 fields,
186 unknown_attribute_policy,
187 unknown_child_policy,
188 discard_attr,
189 discard_text,
190 selected_attributes: selected_attributes.into_iter().collect(),
191 })
192 }
193
194 pub(crate) fn from_fields(
196 compound_fields: &Fields,
197 container_namespace: &NamespaceRef,
198 unknown_attribute_policy: Option<Ident>,
199 unknown_child_policy: Option<Ident>,
200 discard: Vec<DiscardSpec>,
201 ) -> Result<Self> {
202 Self::from_field_defs(
203 compound_fields.iter().enumerate().map(|(i, field)| {
204 let index = match i.try_into() {
205 Ok(v) => v,
206 Err(_) => {
209 return Err(Error::new_spanned(
210 field,
211 "okay, mate, that are way too many fields. get your life together.",
212 ))
213 }
214 };
215 FieldDef::from_field(field, index, container_namespace)
216 }),
217 unknown_attribute_policy,
218 unknown_child_policy,
219 discard,
220 )
221 }
222
223 fn assert_disjunct_attributes(&self) -> TokenStream {
228 let mut checks = TokenStream::default();
229
230 for (i, (qname_a, member_a)) in self.selected_attributes.iter().enumerate() {
235 for (j, (qname_b, member_b)) in self.selected_attributes.iter().enumerate() {
236 if i == j {
237 continue;
238 }
239 let span = member_a.span();
245 let (member_a, member_b) = if i > j {
246 (member_b, member_a)
247 } else {
248 (member_a, member_b)
249 };
250 if qname_a.namespace.is_some() != qname_b.namespace.is_some() {
251 continue;
253 }
254 let Some((name_a, name_b)) = qname_a.name.as_ref().zip(qname_b.name.as_ref())
255 else {
256 panic!("selected attribute has no XML local name");
257 };
258
259 let mut check = quote! {
260 ::xso::exports::const_str_eq(#name_a.as_str(), #name_b.as_str())
261 };
262
263 let namespaces = qname_a.namespace.as_ref().zip(qname_b.namespace.as_ref());
264 if let Some((ns_a, ns_b)) = namespaces {
265 check.extend(quote! {
266 && ::xso::exports::const_str_eq(#ns_a, #ns_b)
267 });
268 };
269
270 let attr_a = if let Some(namespace_a) = qname_a.namespace.as_ref() {
271 format!("{{{}}}{}", namespace_a, name_a)
272 } else {
273 format!("{}", name_a)
274 };
275
276 let attr_b = if let Some(namespace_b) = qname_b.namespace.as_ref() {
277 format!("{{{}}}{}", namespace_b, name_b)
278 } else {
279 format!("{}", name_b)
280 };
281
282 let attr_a = attr_a.replace('{', "{{").replace('}', "}}");
290 let attr_b = attr_b.replace('{', "{{").replace('}', "}}");
291 let field_a = FieldName(member_a)
292 .to_string()
293 .replace('{', "{{")
294 .replace('}', "}}");
295 let field_b = FieldName(member_b)
296 .to_string()
297 .replace('{', "{{")
298 .replace('}', "}}");
299
300 checks.extend(quote_spanned! {span=>
304 const _: () = { if #check {
305 panic!(concat!("member ", #field_a, " and member ", #field_b, " match the same XML attribute: ", #attr_a, " == ", #attr_b));
306 } };
307 })
308 }
309 }
310
311 checks
312 }
313
314 pub(crate) fn make_from_events_statemachine(
320 &self,
321 generics: &mut GenericsInfo,
322 state_ty_ident: &Ident,
323 output_name: &ParentRef,
324 state_prefix: &str,
325 ) -> Result<FromEventsSubmachine> {
326 let scope = FromEventsScope::new(state_ty_ident.clone());
327 let FromEventsScope {
328 ref attrs,
329 ref builder_data_ident,
330 ref text,
331 ref substate_data,
332 ref substate_result,
333 ..
334 } = scope;
335
336 let default_state_ident = quote::format_ident!("{}Default", state_prefix);
337 let discard_state_ident = quote::format_ident!("{}Discard", state_prefix);
338 let builder_data_ty_ident = quote::format_ident!("{}Data{}", state_ty_ident, state_prefix);
339 let builder_data_ty = generics.ty_with_arguments(builder_data_ty_ident.clone());
340 let mut states = Vec::new();
341
342 let mut builder_data_def = TokenStream::default();
343 let mut builder_data_init = TokenStream::default();
344 let mut output_cons = TokenStream::default();
345 let mut child_matchers = TokenStream::default();
346 let mut fallback_child_matcher = None;
347 let mut text_handler = if self.discard_text.is_set() {
348 Some(quote! {
349 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
350 Self::#default_state_ident { #builder_data_ident }
351 ))
352 })
353 } else {
354 None
355 };
356 let mut extra_defs = TokenStream::default();
357 let is_tuple = !output_name.is_path();
358
359 for (i, field) in self.fields.iter().enumerate() {
360 let member = field.member();
361 let builder_field_name = mangle_member(member);
362 let part = field.make_builder_part(&scope, generics, output_name)?;
363 let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
364
365 match part {
366 FieldBuilderPart::Init {
367 value: FieldTempInit { ty, init },
368 } => {
369 builder_data_def.extend(quote! {
370 #builder_field_name: #ty,
371 });
372
373 builder_data_init.extend(quote! {
374 #builder_field_name: #init,
375 });
376
377 if is_tuple {
378 output_cons.extend(quote! {
379 #builder_data_ident.#builder_field_name,
380 });
381 } else {
382 output_cons.extend(quote! {
383 #member: #builder_data_ident.#builder_field_name,
384 });
385 }
386 }
387
388 FieldBuilderPart::Text {
389 value: FieldTempInit { ty, init },
390 collect,
391 finalize,
392 } => {
393 if text_handler.is_some() {
394 panic!("more than one field attempts to collect text data");
397 }
398
399 builder_data_def.extend(quote! {
400 #builder_field_name: #ty,
401 });
402 builder_data_init.extend(quote! {
403 #builder_field_name: #init,
404 });
405 text_handler = Some(quote! {
406 #collect
407 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
408 Self::#default_state_ident { #builder_data_ident }
409 ))
410 });
411
412 if is_tuple {
413 output_cons.extend(quote! {
414 #finalize,
415 });
416 } else {
417 output_cons.extend(quote! {
418 #member: #finalize,
419 });
420 }
421 }
422
423 FieldBuilderPart::Nested {
424 extra_defs: field_extra_defs,
425 value: FieldTempInit { ty, init },
426 matcher,
427 builder,
428 collect,
429 finalize,
430 } => {
431 let feed = feed_fn(builder.clone());
432
433 let mut substate_data_ident = substate_data.clone();
434 substate_data_ident.set_span(ty.span());
435 states.push(State::new_with_builder(
436 state_name.clone(),
437 builder_data_ident,
438 &builder_data_ty,
439 ).with_field(
440 &substate_data_ident,
441 &builder,
442 ).with_mut(substate_data).with_impl(quote! {
443 match #feed(&mut #substate_data, ev, ctx)? {
444 ::core::option::Option::Some(#substate_result) => {
445 #collect
446 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
447 #builder_data_ident,
448 }))
449 }
450 ::core::option::Option::None => {
451 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
452 #builder_data_ident,
453 #substate_data,
454 }))
455 }
456 }
457 }));
458
459 builder_data_def.extend(quote! {
460 #builder_field_name: #ty,
461 });
462
463 builder_data_init.extend(quote! {
464 #builder_field_name: #init,
465 });
466
467 match matcher {
468 NestedMatcher::Selective(matcher) => {
469 child_matchers.extend(quote! {
470 let (name, attrs) = match #matcher {
471 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs }) => (name, attrs),
472 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(e)) => return ::core::result::Result::Err(e),
473 ::core::result::Result::Ok(#substate_data) => {
474 return ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
475 #builder_data_ident,
476 #substate_data,
477 }))
478 }
479 };
480 });
481 }
482 NestedMatcher::Fallback(matcher) => {
483 if let Some((span, _)) = fallback_child_matcher.as_ref() {
484 let mut err = Error::new(
485 field.span(),
486 "more than one field is attempting to consume all unmatched child elements"
487 );
488 err.combine(Error::new(
489 *span,
490 "the previous field collecting all unmatched child elements is here"
491 ));
492 return Err(err);
493 }
494
495 let matcher = quote! {
496 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
497 #builder_data_ident,
498 #substate_data: { #matcher },
499 }))
500 };
501
502 fallback_child_matcher = Some((field.span(), matcher));
503 }
504 }
505
506 if is_tuple {
507 output_cons.extend(quote! {
508 #finalize,
509 });
510 } else {
511 output_cons.extend(quote! {
512 #member: #finalize,
513 });
514 }
515
516 extra_defs.extend(field_extra_defs);
517 }
518 }
519 }
520
521 let mut discard_attr = quote! {
524 let _ = #attrs.remove(::xso::exports::rxml::Namespace::xml(), "lang");
525 };
526 for (xml_namespace, xml_name) in self.discard_attr.iter() {
527 let xml_namespace = match xml_namespace {
528 Some(v) => v.to_token_stream(),
529 None => quote! {
530 ::xso::exports::rxml::Namespace::none()
531 },
532 };
533 discard_attr.extend(quote! {
534 let _ = #attrs.remove(#xml_namespace, #xml_name);
535 });
536 }
537
538 let text_handler = match text_handler {
539 Some(v) => v,
540 None => quote! {
541 if !::xso::is_xml_whitespace(#text.as_bytes()) {
544 ::core::result::Result::Err(::xso::error::Error::Other("Unexpected text content".into()))
545 } else {
546 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
547 Self::#default_state_ident { #builder_data_ident }
548 ))
549 }
550 },
551 };
552
553 let unknown_attr_err = format!("Unknown attribute in {}.", output_name);
554 let unknown_child_err = format!("Unknown child in {}.", output_name);
555 let unknown_child_policy = &self.unknown_child_policy;
556
557 let output_cons = match output_name {
558 ParentRef::Named(ref path) => {
559 quote! {
560 #path { #output_cons }
561 }
562 }
563 ParentRef::Unnamed { .. } => {
564 quote! {
565 ( #output_cons )
566 }
567 }
568 };
569
570 let discard_builder_ty = discard_builder_ty(Span::call_site());
571 let discard_feed = feed_fn(discard_builder_ty.clone());
572 let child_fallback = match fallback_child_matcher {
573 Some((_, matcher)) => matcher,
574 None => quote! {
575 let _ = (name, attrs);
576 let _: () = #unknown_child_policy.apply_policy(#unknown_child_err)?;
577 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
578 #builder_data_ident,
579 #substate_data: #discard_builder_ty::new(),
580 }))
581 },
582 };
583
584 states.push(State::new_with_builder(
585 discard_state_ident.clone(),
586 builder_data_ident,
587 &builder_data_ty,
588 ).with_field(
589 substate_data,
590 &discard_builder_ty,
591 ).with_mut(substate_data).with_impl(quote! {
592 match #discard_feed(&mut #substate_data, ev, ctx)? {
593 ::core::option::Option::Some(#substate_result) => {
594 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
595 #builder_data_ident,
596 }))
597 }
598 ::core::option::Option::None => {
599 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
600 #builder_data_ident,
601 #substate_data,
602 }))
603 }
604 }
605 }));
606
607 states.push(State::new_with_builder(
608 default_state_ident.clone(),
609 builder_data_ident,
610 &builder_data_ty,
611 ).with_impl(quote! {
612 match ev {
613 ::xso::exports::rxml::Event::EndElement(_) => {
615 ::core::result::Result::Ok(::core::ops::ControlFlow::Continue(
616 #output_cons
617 ))
618 }
619 ::xso::exports::rxml::Event::StartElement(_, name, attrs) => {
620 #child_matchers
621 #child_fallback
622 }
623 ::xso::exports::rxml::Event::Text(_, #text) => {
624 #text_handler
625 }
626 ::xso::exports::rxml::Event::XmlDeclaration(_, ::xso::exports::rxml::XmlVersion::V1_0) => ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
631 Self::#default_state_ident { #builder_data_ident }
632 ))
633 }
634 }));
635
636 let unknown_attribute_policy = &self.unknown_attribute_policy;
637
638 let checks = self.assert_disjunct_attributes();
639
640 Ok(FromEventsSubmachine {
641 defs: quote! {
642 #extra_defs
643 #checks
644
645 struct #builder_data_ty {
646 #builder_data_def
647 }
648 },
649 states,
650 init: quote! {
651 let #builder_data_ident = #builder_data_ty_ident {
652 #builder_data_init
653 };
654 #discard_attr
655 if #attrs.len() > 0 {
656 let _: () = #unknown_attribute_policy.apply_policy(#unknown_attr_err)?;
657 }
658 ::core::result::Result::Ok(#state_ty_ident::#default_state_ident { #builder_data_ident })
659 },
660 })
661 }
662
663 pub(crate) fn make_as_item_iter_statemachine(
673 &self,
674 generics: &mut GenericsInfo,
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, generics, 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}