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