1use proc_macro2::{Span, TokenStream};
13use quote::{quote, quote_spanned};
14use syn::{spanned::Spanned, *};
15
16use crate::common::GenericsInfo;
17use crate::compound::Compound;
18use crate::error_message::{self, ParentRef};
19use crate::meta::{AmountConstraint, Flag, NameRef, NamespaceRef};
20use crate::scope::{AsItemsScope, FromEventsScope};
21use crate::types::{
22 as_xml_iter_fn, as_xml_trait, default_fn, extend_fn, from_events_fn, from_xml_builder_ty,
23 from_xml_trait, into_iterator_into_iter_fn, into_iterator_item_ty, into_iterator_iter_ty,
24 item_iter_ty, option_as_xml_ty, option_ty, ref_ty,
25};
26
27use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit, NestedMatcher};
28
29pub(super) struct ChildField {
31 pub(super) default_: Flag,
34
35 pub(super) amount: AmountConstraint,
37
38 pub(super) extract: Option<ExtractDef>,
41}
42
43impl Field for ChildField {
44 fn make_builder_part(
45 &self,
46 scope: &FromEventsScope,
47 generics: &mut GenericsInfo,
48 container_name: &ParentRef,
49 member: &Member,
50 ty: &Type,
51 ) -> Result<FieldBuilderPart> {
52 let (element_ty, is_container) = match self.amount {
53 AmountConstraint::FixedSingle(_) => (ty.clone(), false),
54 AmountConstraint::Any(_) => (into_iterator_item_ty(ty.clone()), true),
55 };
56
57 let (extra_defs, matcher, fetch, builder) = match self.extract {
58 Some(ref extract) => extract.make_from_xml_builder_parts(
59 scope,
60 generics,
61 container_name,
62 member,
63 is_container,
64 ty,
65 )?,
66 None => {
67 let FromEventsScope {
68 ref substate_result,
69 ..
70 } = scope;
71
72 generics.add_bound_if_generic(&element_ty, from_xml_trait(Span::call_site()));
73 let from_events = from_events_fn(element_ty.clone());
74
75 let span = element_ty.span();
76 let matcher = quote_spanned! { span=> #from_events(name, attrs, ctx) };
77 let builder = from_xml_builder_ty(element_ty.clone());
78
79 (
80 TokenStream::default(),
81 matcher,
82 quote! { #substate_result },
83 builder,
84 )
85 }
86 };
87
88 let field_access = scope.access_field(member);
89 match self.amount {
90 AmountConstraint::FixedSingle(_) => {
91 let missing_msg = error_message::on_missing_child(container_name, member);
92 let duplicate_msg = error_message::on_duplicate_child(container_name, member);
93
94 let on_absent = match self.default_ {
95 Flag::Absent => quote! {
96 return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
97 },
98 Flag::Present(_) => {
99 let default_ = default_fn(element_ty.clone());
100 quote! {
101 #default_()
102 }
103 }
104 };
105
106 Ok(FieldBuilderPart::Nested {
107 extra_defs,
108 value: FieldTempInit {
109 init: quote! { ::core::option::Option::None },
110 ty: option_ty(ty.clone()),
111 },
112 matcher: NestedMatcher::Selective(quote! {
113 match #matcher {
114 ::core::result::Result::Ok(v) => if #field_access.is_some() {
115 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(::xso::error::Error::Other(#duplicate_msg)))
116 } else {
117 ::core::result::Result::Ok(v)
118 },
119 ::core::result::Result::Err(e) => ::core::result::Result::Err(e),
120 }
121 }),
122 builder,
123 collect: quote! {
124 #field_access = ::core::option::Option::Some(#fetch);
125 },
126 finalize: quote! {
127 match #field_access {
128 ::core::option::Option::Some(value) => value,
129 ::core::option::Option::None => #on_absent,
130 }
131 },
132 })
133 }
134 AmountConstraint::Any(_) => {
135 let ty_extend = extend_fn(ty.clone(), element_ty.clone());
136 let ty_default = default_fn(ty.clone());
137 Ok(FieldBuilderPart::Nested {
138 extra_defs,
139 value: FieldTempInit {
140 init: quote! { #ty_default() },
141 ty: ty.clone(),
142 },
143 matcher: NestedMatcher::Selective(matcher),
144 builder,
145 collect: quote! {
146 #ty_extend(&mut #field_access, [#fetch]);
147 },
148 finalize: quote! { #field_access },
149 })
150 }
151 }
152 }
153
154 fn make_iterator_part(
155 &self,
156 scope: &AsItemsScope,
157 generics: &mut GenericsInfo,
158 container_name: &ParentRef,
159 bound_name: &Ident,
160 member: &Member,
161 ty: &Type,
162 ) -> Result<FieldIteratorPart> {
163 let AsItemsScope { ref lifetime, .. } = scope;
164
165 let (item_ty, is_container) = match self.amount {
166 AmountConstraint::FixedSingle(_) => (ty.clone(), false),
167 AmountConstraint::Any(_) => {
168 (into_iterator_item_ty(ty.clone()), true)
171 }
172 };
173
174 let (extra_defs, init, iter_ty) = match self.extract {
175 Some(ref extract) => extract.make_as_item_iter_parts(
176 scope,
177 generics,
178 ty,
179 container_name,
180 bound_name,
181 member,
182 is_container,
183 )?,
184 None => {
185 generics.add_bound_if_generic(&item_ty, as_xml_trait(Span::call_site()));
186 let as_xml_iter = as_xml_iter_fn(item_ty.clone());
187 let item_iter = item_iter_ty(item_ty.clone(), lifetime.clone());
188
189 let span = item_ty.span();
190 (
191 TokenStream::default(),
192 quote_spanned! { span=> #as_xml_iter(#bound_name)? },
193 item_iter,
194 )
195 }
196 };
197
198 match self.amount {
199 AmountConstraint::FixedSingle(_) => Ok(FieldIteratorPart::Content {
200 extra_defs,
201 value: FieldTempInit { init, ty: iter_ty },
202 generator: quote! {
203 #bound_name.next().transpose()
204 },
205 }),
206 AmountConstraint::Any(_) => {
207 let ty = ref_ty(ty.clone(), lifetime.clone());
210
211 let element_iter = into_iterator_iter_ty(ty.clone());
214
215 let into_iter = into_iterator_into_iter_fn(ty.clone());
217
218 let state_ty = Type::Tuple(TypeTuple {
219 paren_token: token::Paren::default(),
220 elems: [element_iter, option_ty(iter_ty)].into_iter().collect(),
221 });
222
223 Ok(FieldIteratorPart::Content {
224 extra_defs,
225 value: FieldTempInit {
226 init: quote! {
227 (#into_iter(#bound_name), ::core::option::Option::None)
228 },
229 ty: state_ty,
230 },
231 generator: quote! {
232 loop {
233 if let ::core::option::Option::Some(current) = #bound_name.1.as_mut() {
234 if let ::core::option::Option::Some(item) = current.next() {
235 break ::core::option::Option::Some(item).transpose();
236 }
237 }
238 if let ::core::option::Option::Some(item) = #bound_name.0.next() {
239 #bound_name.1 = ::core::option::Option::Some({
240 let #bound_name = item;
241 #init
242 });
243 } else {
244 break ::core::result::Result::Ok(::core::option::Option::None)
245 }
246 }
247 },
248 })
249 }
250 }
251 }
252}
253
254pub(super) struct ExtractDef {
256 pub(super) xml_namespace: NamespaceRef,
258
259 pub(super) xml_name: NameRef,
261
262 pub(super) parts: Compound,
272}
273
274impl ExtractDef {
275 fn make_from_xml_builder_parts(
282 &self,
283 scope: &FromEventsScope,
284 generics: &GenericsInfo,
285 container_name: &ParentRef,
286 member: &Member,
287 collecting_into_container: bool,
288 output_ty: &Type,
289 ) -> Result<(TokenStream, TokenStream, TokenStream, Type)> {
290 let FromEventsScope {
291 ref substate_result,
292 ..
293 } = scope;
294
295 let xml_namespace = &self.xml_namespace;
296 let xml_name = &self.xml_name;
297
298 let from_xml_builder_ty_ident = scope.make_member_type_name(member, "FromXmlBuilder");
299 let state_ty_ident = quote::format_ident!("{}State", from_xml_builder_ty_ident,);
300
301 let inner_ty = self.parts.to_single_or_tuple_ty();
302 let mut generics = generics.scoped_for(&inner_ty)?;
303 let extra_defs = self.parts.make_from_events_statemachine(
304 &mut generics,
305 &state_ty_ident,
306 &container_name.child(member.clone()),
307 "",
308 )?.with_augmented_init(|init| quote! {
309 if name.0 == #xml_namespace && name.1 == #xml_name {
310 #init
311 } else {
312 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs })
313 }
314 }).compile().render(
315 &Visibility::Inherited,
316 &generics,
317 &from_xml_builder_ty_ident,
318 &state_ty_ident,
319 &self.parts.to_tuple_ty().into(),
320 None,
321 )?;
322 let from_xml_builder_ty = generics.ty_with_arguments(from_xml_builder_ty_ident.clone());
323
324 let matcher = quote! { #state_ty_ident::new(name, attrs, ctx).map(|x| #from_xml_builder_ty_ident(::core::option::Option::Some(x))) };
325
326 let fetch = if self.parts.field_count() == 1 {
327 quote! { #substate_result.0 }
330 } else {
331 quote! { #substate_result }
334 };
335
336 let fetch = if collecting_into_container {
337 fetch
341 } else {
342 quote_spanned! {
361 output_ty.span()=>
362 <#output_ty as ::core::convert::From::<#inner_ty>>::from(#fetch)
363 }
364 };
365
366 Ok((extra_defs, matcher, fetch, from_xml_builder_ty))
367 }
368
369 fn make_as_item_iter_parts(
374 &self,
375 scope: &AsItemsScope,
376 generics: &GenericsInfo,
377 input_ty: &Type,
378 container_name: &ParentRef,
379 bound_name: &Ident,
380 member: &Member,
381 iterating_container: bool,
382 ) -> Result<(TokenStream, TokenStream, Type)> {
383 let AsItemsScope { ref lifetime, .. } = scope;
384
385 let xml_namespace = &self.xml_namespace;
386 let xml_name = &self.xml_name;
387
388 let item_iter_ty_ident = scope.make_member_type_name(member, "AsXmlIterator");
389 let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident,);
390 let tuple_ty = self.parts.to_ref_tuple_ty(lifetime);
391
392 let (repack, inner_ty) = match self.parts.single_ty() {
393 Some(single_ty) => (
394 quote! { #bound_name, },
395 ref_ty(single_ty.clone(), lifetime.clone()),
396 ),
397 None => {
398 let mut repack_tuple = TokenStream::default();
399 for i in 0..(tuple_ty.elems.len() as u32) {
403 let index = Index {
404 index: i,
405 span: Span::call_site(),
406 };
407 repack_tuple.extend(quote! {
408 &#bound_name.#index,
409 })
410 }
411 let ref_tuple_ty = ref_ty(self.parts.to_tuple_ty().into(), lifetime.clone());
412 (repack_tuple, ref_tuple_ty)
413 }
414 };
415
416 let mut generics = generics.scoped_for(&ref_ty(inner_ty.clone(), lifetime.clone()))?;
417
418 let item_iter_ty = generics.ty_with_arguments(item_iter_ty_ident.clone());
419 let extra_defs = self
420 .parts
421 .make_as_item_iter_statemachine(
422 &mut generics,
423 &container_name.child(member.clone()),
424 &state_ty_ident,
425 "",
426 lifetime,
427 )?
428 .with_augmented_init(|init| {
429 quote! {
430 let name = (
431 ::xso::exports::rxml::Namespace::from(#xml_namespace),
432 ::xso::exports::alloc::borrow::Cow::Borrowed(#xml_name),
433 );
434 #init
435 }
436 })
437 .compile()
438 .render(
439 &Visibility::Inherited,
440 &generics,
441 &tuple_ty.into(),
442 &state_ty_ident,
443 lifetime,
444 &item_iter_ty_ident,
445 )?;
446
447 let (make_iter, item_iter_ty) = if iterating_container {
448 (
459 quote! {
460 #item_iter_ty_ident::new((#repack))?
461 },
462 item_iter_ty,
463 )
464 } else {
465 let cast = quote_spanned! { input_ty.span()=>
476 ::core::option::Option::from(#bound_name)
477 };
478 let type_assert = quote_spanned! { inner_ty.span()=>
479 ::core::option::Option<#inner_ty>
480 };
481 (
482 quote! {
483 ::xso::asxml::OptionAsXml::new({ let x: #type_assert = #cast; x.map(|#bound_name: #inner_ty| {
484 #item_iter_ty_ident::new((#repack))
485 })}.transpose()?)
486 },
487 option_as_xml_ty(item_iter_ty),
488 )
489 };
490
491 Ok((extra_defs, make_iter, item_iter_ty))
492 }
493}