xso_proc/field/
element.rs1use proc_macro2::{Span, TokenStream};
13use quote::quote;
14use syn::*;
15
16use crate::common::GenericsInfo;
17use crate::error_message::{self, ParentRef};
18use crate::meta::{AmountConstraint, Flag};
19use crate::scope::{AsItemsScope, FromEventsScope};
20use crate::types::{
21 as_xml_iter_fn, default_fn, element_ty, from_events_fn, from_xml_builder_ty,
22 into_iterator_into_iter_fn, into_iterator_item_ty, into_iterator_iter_ty, item_iter_ty,
23 option_ty, ref_ty,
24};
25
26use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit, NestedMatcher};
27
28pub(super) struct ElementField {
29 pub(super) default_: Flag,
32
33 pub(super) amount: AmountConstraint,
35}
36
37impl Field for ElementField {
38 fn make_builder_part(
39 &self,
40 scope: &FromEventsScope,
41 _generics: &mut GenericsInfo,
42 container_name: &ParentRef,
43 member: &Member,
44 ty: &Type,
45 ) -> Result<FieldBuilderPart> {
46 let element_ty = match self.amount {
47 AmountConstraint::FixedSingle(_) => ty.clone(),
48 AmountConstraint::Any(_) => into_iterator_item_ty(ty.clone()),
49 };
50
51 let FromEventsScope {
52 ref substate_result,
53 ..
54 } = scope;
55
56 let from_events = from_events_fn(element_ty.clone());
57
58 let extra_defs = TokenStream::default();
59 let field_access = scope.access_field(member);
60
61 let default_fn = default_fn(ty.clone());
62 let builder = from_xml_builder_ty(element_ty.clone());
63
64 match self.amount {
65 AmountConstraint::FixedSingle(_) => {
66 let missing_msg = error_message::on_missing_child(container_name, member);
67 let on_absent = match self.default_ {
68 Flag::Absent => quote! {
69 return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
70 },
71 Flag::Present(_) => {
72 quote! { #default_fn() }
73 }
74 };
75 Ok(FieldBuilderPart::Nested {
76 extra_defs,
77 value: FieldTempInit {
78 init: quote! { ::core::option::Option::None },
79 ty: option_ty(ty.clone()),
80 },
81 matcher: NestedMatcher::Selective(quote! {
82 if #field_access.is_some() {
83 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs })
84 } else {
85 #from_events(name, attrs, ctx)
86 }
87 }),
88 builder,
89 collect: quote! {
90 #field_access = ::core::option::Option::Some(#substate_result);
91 },
92 finalize: quote! {
93 match #field_access {
94 ::core::option::Option::Some(value) => value,
95 ::core::option::Option::None => #on_absent,
96 }
97 },
98 })
99 }
100 AmountConstraint::Any(_) => Ok(FieldBuilderPart::Nested {
101 extra_defs,
102 value: FieldTempInit {
103 init: quote! { #default_fn() },
104 ty: ty.clone(),
105 },
106 matcher: NestedMatcher::Fallback(quote! {
107 #builder::new(name, attrs)
108 }),
109 builder,
110 collect: quote! {
111 <#ty as ::core::iter::Extend::<#element_ty>>::extend(&mut #field_access, [#substate_result]);
112 },
113 finalize: quote! {
114 #field_access
115 },
116 }),
117 }
118 }
119
120 fn make_iterator_part(
121 &self,
122 scope: &AsItemsScope,
123 _generics: &mut GenericsInfo,
124 _container_name: &ParentRef,
125 bound_name: &Ident,
126 _member: &Member,
127 ty: &Type,
128 ) -> Result<FieldIteratorPart> {
129 let AsItemsScope { ref lifetime, .. } = scope;
130
131 let item_ty = match self.amount {
132 AmountConstraint::FixedSingle(_) => ty.clone(),
133 AmountConstraint::Any(_) => {
134 into_iterator_item_ty(ty.clone())
137 }
138 };
139
140 let element_ty = element_ty(Span::call_site());
141 let iter_ty = item_iter_ty(element_ty.clone(), lifetime.clone());
142 let element_iter = into_iterator_iter_ty(ref_ty(ty.clone(), lifetime.clone()));
143 let into_iter = into_iterator_into_iter_fn(ref_ty(ty.clone(), lifetime.clone()));
144
145 let state_ty = Type::Tuple(TypeTuple {
146 paren_token: token::Paren::default(),
147 elems: [element_iter, option_ty(iter_ty.clone())]
148 .into_iter()
149 .collect(),
150 });
151
152 let extra_defs = TokenStream::default();
153 let as_xml_iter = as_xml_iter_fn(item_ty.clone());
154 let init = quote! { #as_xml_iter(#bound_name)? };
155 let iter_ty = item_iter_ty(item_ty.clone(), lifetime.clone());
156
157 match self.amount {
158 AmountConstraint::FixedSingle(_) => Ok(FieldIteratorPart::Content {
159 extra_defs,
160 value: FieldTempInit { init, ty: iter_ty },
161 generator: quote! {
162 #bound_name.next().transpose()
163 },
164 }),
165 AmountConstraint::Any(_) => Ok(FieldIteratorPart::Content {
166 extra_defs,
167 value: FieldTempInit {
168 init: quote! {
169 (#into_iter(#bound_name), ::core::option::Option::None)
170 },
171 ty: state_ty,
172 },
173 generator: quote! {
174 loop {
175 if let ::core::option::Option::Some(current) = #bound_name.1.as_mut() {
176 if let ::core::option::Option::Some(item) = current.next() {
177 break ::core::option::Option::Some(item).transpose();
178 }
179 }
180 if let ::core::option::Option::Some(item) = #bound_name.0.next() {
181 #bound_name.1 = ::core::option::Option::Some(
182 <#element_ty as ::xso::AsXml>::as_xml_iter(item)?
183 );
184 } else {
185 break ::core::result::Result::Ok(::core::option::Option::None)
186 }
187 }
188 },
189 }),
190 }
191 }
192}