1use proc_macro2::{Span, TokenStream};
13use quote::{quote, quote_spanned};
14use syn::{spanned::Spanned, *};
15
16use crate::compound::Compound;
17use crate::error_message::{self, ParentRef};
18use crate::meta::{AmountConstraint, Flag, NameRef, NamespaceRef};
19use crate::scope::{AsItemsScope, FromEventsScope};
20use crate::types::{
21 as_xml_iter_fn, default_fn, extend_fn, 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_as_xml_ty, option_ty, ref_ty, ty_from_ident,
24};
25
26use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit, NestedMatcher};
27
28pub(super) struct ChildField {
30 pub(super) default_: Flag,
33
34 pub(super) amount: AmountConstraint,
36
37 pub(super) extract: Option<ExtractDef>,
40}
41
42impl Field for ChildField {
43 fn make_builder_part(
44 &self,
45 scope: &FromEventsScope,
46 container_name: &ParentRef,
47 member: &Member,
48 ty: &Type,
49 ) -> Result<FieldBuilderPart> {
50 let (element_ty, is_container) = match self.amount {
51 AmountConstraint::FixedSingle(_) => (ty.clone(), false),
52 AmountConstraint::Any(_) => (into_iterator_item_ty(ty.clone()), true),
53 };
54
55 let (extra_defs, matcher, fetch, builder) = match self.extract {
56 Some(ref extract) => extract.make_from_xml_builder_parts(
57 scope,
58 container_name,
59 member,
60 is_container,
61 ty,
62 )?,
63 None => {
64 let FromEventsScope {
65 ref substate_result,
66 ..
67 } = scope;
68
69 let from_events = from_events_fn(element_ty.clone());
70
71 let matcher = quote! { #from_events(name, attrs) };
72 let builder = from_xml_builder_ty(element_ty.clone());
73
74 (
75 TokenStream::default(),
76 matcher,
77 quote! { #substate_result },
78 builder,
79 )
80 }
81 };
82
83 let field_access = scope.access_field(member);
84 match self.amount {
85 AmountConstraint::FixedSingle(_) => {
86 let missing_msg = error_message::on_missing_child(container_name, member);
87 let duplicate_msg = error_message::on_duplicate_child(container_name, member);
88
89 let on_absent = match self.default_ {
90 Flag::Absent => quote! {
91 return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
92 },
93 Flag::Present(_) => {
94 let default_ = default_fn(element_ty.clone());
95 quote! {
96 #default_()
97 }
98 }
99 };
100
101 Ok(FieldBuilderPart::Nested {
102 extra_defs,
103 value: FieldTempInit {
104 init: quote! { ::core::option::Option::None },
105 ty: option_ty(ty.clone()),
106 },
107 matcher: NestedMatcher::Selective(quote! {
108 match #matcher {
109 ::core::result::Result::Ok(v) => if #field_access.is_some() {
110 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(::xso::error::Error::Other(#duplicate_msg)))
111 } else {
112 ::core::result::Result::Ok(v)
113 },
114 ::core::result::Result::Err(e) => ::core::result::Result::Err(e),
115 }
116 }),
117 builder,
118 collect: quote! {
119 #field_access = ::core::option::Option::Some(#fetch);
120 },
121 finalize: quote! {
122 match #field_access {
123 ::core::option::Option::Some(value) => value,
124 ::core::option::Option::None => #on_absent,
125 }
126 },
127 })
128 }
129 AmountConstraint::Any(_) => {
130 let ty_extend = extend_fn(ty.clone(), element_ty.clone());
131 let ty_default = default_fn(ty.clone());
132 Ok(FieldBuilderPart::Nested {
133 extra_defs,
134 value: FieldTempInit {
135 init: quote! { #ty_default() },
136 ty: ty.clone(),
137 },
138 matcher: NestedMatcher::Selective(matcher),
139 builder,
140 collect: quote! {
141 #ty_extend(&mut #field_access, [#fetch]);
142 },
143 finalize: quote! { #field_access },
144 })
145 }
146 }
147 }
148
149 fn make_iterator_part(
150 &self,
151 scope: &AsItemsScope,
152 container_name: &ParentRef,
153 bound_name: &Ident,
154 member: &Member,
155 ty: &Type,
156 ) -> Result<FieldIteratorPart> {
157 let AsItemsScope { ref lifetime, .. } = scope;
158
159 let (item_ty, is_container) = match self.amount {
160 AmountConstraint::FixedSingle(_) => (ty.clone(), false),
161 AmountConstraint::Any(_) => {
162 (into_iterator_item_ty(ty.clone()), true)
165 }
166 };
167
168 let (extra_defs, init, iter_ty) = match self.extract {
169 Some(ref extract) => extract.make_as_item_iter_parts(
170 scope,
171 ty,
172 container_name,
173 bound_name,
174 member,
175 is_container,
176 )?,
177 None => {
178 let as_xml_iter = as_xml_iter_fn(item_ty.clone());
179 let item_iter = item_iter_ty(item_ty.clone(), lifetime.clone());
180
181 (
182 TokenStream::default(),
183 quote! { #as_xml_iter(#bound_name)? },
184 item_iter,
185 )
186 }
187 };
188
189 match self.amount {
190 AmountConstraint::FixedSingle(_) => Ok(FieldIteratorPart::Content {
191 extra_defs,
192 value: FieldTempInit { init, ty: iter_ty },
193 generator: quote! {
194 #bound_name.next().transpose()
195 },
196 }),
197 AmountConstraint::Any(_) => {
198 let ty = ref_ty(ty.clone(), lifetime.clone());
201
202 let element_iter = into_iterator_iter_ty(ty.clone());
205
206 let into_iter = into_iterator_into_iter_fn(ty.clone());
208
209 let state_ty = Type::Tuple(TypeTuple {
210 paren_token: token::Paren::default(),
211 elems: [element_iter, option_ty(iter_ty)].into_iter().collect(),
212 });
213
214 Ok(FieldIteratorPart::Content {
215 extra_defs,
216 value: FieldTempInit {
217 init: quote! {
218 (#into_iter(#bound_name), ::core::option::Option::None)
219 },
220 ty: state_ty,
221 },
222 generator: quote! {
223 loop {
224 if let ::core::option::Option::Some(current) = #bound_name.1.as_mut() {
225 if let ::core::option::Option::Some(item) = current.next() {
226 break ::core::option::Option::Some(item).transpose();
227 }
228 }
229 if let ::core::option::Option::Some(item) = #bound_name.0.next() {
230 #bound_name.1 = ::core::option::Option::Some({
231 let #bound_name = item;
232 #init
233 });
234 } else {
235 break ::core::result::Result::Ok(::core::option::Option::None)
236 }
237 }
238 },
239 })
240 }
241 }
242 }
243}
244
245pub(super) struct ExtractDef {
247 pub(super) xml_namespace: NamespaceRef,
249
250 pub(super) xml_name: NameRef,
252
253 pub(super) parts: Compound,
263}
264
265impl ExtractDef {
266 fn make_from_xml_builder_parts(
273 &self,
274 scope: &FromEventsScope,
275 container_name: &ParentRef,
276 member: &Member,
277 collecting_into_container: bool,
278 output_ty: &Type,
279 ) -> Result<(TokenStream, TokenStream, TokenStream, Type)> {
280 let FromEventsScope {
281 ref substate_result,
282 ..
283 } = scope;
284
285 let xml_namespace = &self.xml_namespace;
286 let xml_name = &self.xml_name;
287
288 let from_xml_builder_ty_ident = scope.make_member_type_name(member, "FromXmlBuilder");
289 let state_ty_ident = quote::format_ident!("{}State", from_xml_builder_ty_ident,);
290
291 let extra_defs = self.parts.make_from_events_statemachine(
292 &state_ty_ident,
293 &container_name.child(member.clone()),
294 "",
295 )?.with_augmented_init(|init| quote! {
296 if name.0 == #xml_namespace && name.1 == #xml_name {
297 #init
298 } else {
299 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs })
300 }
301 }).compile().render(
302 &Visibility::Inherited,
303 &from_xml_builder_ty_ident,
304 &state_ty_ident,
305 &self.parts.to_tuple_ty().into(),
306 )?;
307 let from_xml_builder_ty = ty_from_ident(from_xml_builder_ty_ident.clone()).into();
308
309 let matcher = quote! { #state_ty_ident::new(name, attrs).map(|x| #from_xml_builder_ty_ident(::core::option::Option::Some(x))) };
310
311 let inner_ty = self.parts.to_single_or_tuple_ty();
312
313 let fetch = if self.parts.field_count() == 1 {
314 quote! { #substate_result.0 }
317 } else {
318 quote! { #substate_result }
321 };
322
323 let fetch = if collecting_into_container {
324 fetch
328 } else {
329 quote_spanned! {
348 output_ty.span()=>
349 <#output_ty as ::core::convert::From::<#inner_ty>>::from(#fetch)
350 }
351 };
352
353 Ok((extra_defs, matcher, fetch, from_xml_builder_ty))
354 }
355
356 fn make_as_item_iter_parts(
361 &self,
362 scope: &AsItemsScope,
363 input_ty: &Type,
364 container_name: &ParentRef,
365 bound_name: &Ident,
366 member: &Member,
367 iterating_container: bool,
368 ) -> Result<(TokenStream, TokenStream, Type)> {
369 let AsItemsScope { ref lifetime, .. } = scope;
370
371 let xml_namespace = &self.xml_namespace;
372 let xml_name = &self.xml_name;
373
374 let item_iter_ty_ident = scope.make_member_type_name(member, "AsXmlIterator");
375 let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident,);
376 let mut item_iter_ty = ty_from_ident(item_iter_ty_ident.clone());
377 item_iter_ty.path.segments[0].arguments =
378 PathArguments::AngleBracketed(AngleBracketedGenericArguments {
379 colon2_token: None,
380 lt_token: token::Lt::default(),
381 args: [GenericArgument::Lifetime(lifetime.clone())]
382 .into_iter()
383 .collect(),
384 gt_token: token::Gt::default(),
385 });
386 let item_iter_ty = item_iter_ty.into();
387 let tuple_ty = self.parts.to_ref_tuple_ty(lifetime);
388
389 let (repack, inner_ty) = match self.parts.single_ty() {
390 Some(single_ty) => (
391 quote! { #bound_name, },
392 ref_ty(single_ty.clone(), lifetime.clone()),
393 ),
394 None => {
395 let mut repack_tuple = TokenStream::default();
396 for i in 0..(tuple_ty.elems.len() as u32) {
400 let index = Index {
401 index: i,
402 span: Span::call_site(),
403 };
404 repack_tuple.extend(quote! {
405 &#bound_name.#index,
406 })
407 }
408 let ref_tuple_ty = ref_ty(self.parts.to_tuple_ty().into(), lifetime.clone());
409 (repack_tuple, ref_tuple_ty)
410 }
411 };
412
413 let extra_defs = self
414 .parts
415 .make_as_item_iter_statemachine(
416 &container_name.child(member.clone()),
417 &state_ty_ident,
418 "",
419 lifetime,
420 )?
421 .with_augmented_init(|init| {
422 quote! {
423 let name = (
424 ::xso::exports::rxml::Namespace::from(#xml_namespace),
425 ::xso::exports::alloc::borrow::Cow::Borrowed(#xml_name),
426 );
427 #init
428 }
429 })
430 .compile()
431 .render(
432 &Visibility::Inherited,
433 &tuple_ty.into(),
434 &state_ty_ident,
435 lifetime,
436 &item_iter_ty,
437 )?;
438
439 let (make_iter, item_iter_ty) = if iterating_container {
440 (
451 quote! {
452 #item_iter_ty_ident::new((#repack))?
453 },
454 item_iter_ty,
455 )
456 } else {
457 let cast = quote_spanned! { input_ty.span()=>
468 ::core::option::Option::from(#bound_name)
469 };
470 let type_assert = quote_spanned! { inner_ty.span()=>
471 ::core::option::Option<#inner_ty>
472 };
473 (
474 quote! {
475 ::xso::asxml::OptionAsXml::new({ let x: #type_assert = #cast; x.map(|#bound_name: #inner_ty| {
476 #item_iter_ty_ident::new((#repack))
477 })}.transpose()?)
478 },
479 option_as_xml_ty(item_iter_ty),
480 )
481 };
482
483 Ok((extra_defs, make_iter, item_iter_ty))
484 }
485}