1use proc_macro2::{Span, TokenStream};
10use quote::quote;
11use syn::{spanned::Spanned, *};
12
13use crate::error_message::ParentRef;
14use crate::field::{FieldBuilderPart, FieldDef, FieldIteratorPart, FieldTempInit, NestedMatcher};
15use crate::meta::NamespaceRef;
16use crate::scope::{mangle_member, AsItemsScope, FromEventsScope};
17use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State};
18use crate::types::{
19 default_fn, discard_builder_ty, feed_fn, namespace_ty, ncnamestr_cow_ty, phantom_lifetime_ty,
20 ref_ty, unknown_attribute_policy_path, unknown_child_policy_path,
21};
22
23fn resolve_policy(policy: Option<Ident>, mut enum_ref: Path) -> Expr {
24 match policy {
25 Some(ident) => {
26 enum_ref.segments.push(ident.into());
27 Expr::Path(ExprPath {
28 attrs: Vec::new(),
29 qself: None,
30 path: enum_ref,
31 })
32 }
33 None => {
34 let default_fn = default_fn(Type::Path(TypePath {
35 qself: None,
36 path: enum_ref,
37 }));
38 Expr::Call(ExprCall {
39 attrs: Vec::new(),
40 func: Box::new(default_fn),
41 paren_token: token::Paren::default(),
42 args: punctuated::Punctuated::new(),
43 })
44 }
45 }
46}
47
48pub(crate) struct Compound {
50 fields: Vec<FieldDef>,
52
53 unknown_attribute_policy: Expr,
55
56 unknown_child_policy: Expr,
58}
59
60impl Compound {
61 pub(crate) fn from_field_defs<I: IntoIterator<Item = Result<FieldDef>>>(
63 compound_fields: I,
64 unknown_attribute_policy: Option<Ident>,
65 unknown_child_policy: Option<Ident>,
66 ) -> Result<Self> {
67 let unknown_attribute_policy = resolve_policy(
68 unknown_attribute_policy,
69 unknown_attribute_policy_path(Span::call_site()),
70 );
71 let unknown_child_policy = resolve_policy(
72 unknown_child_policy,
73 unknown_child_policy_path(Span::call_site()),
74 );
75 let compound_fields = compound_fields.into_iter();
76 let size_hint = compound_fields.size_hint();
77 let mut fields = Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0));
78 let mut text_field = None;
79 for field in compound_fields {
80 let field = field?;
81
82 if field.is_text_field() {
83 if let Some(other_field) = text_field.as_ref() {
84 let mut err = Error::new_spanned(
85 field.member(),
86 "only one `#[xml(text)]` field allowed per compound",
87 );
88 err.combine(Error::new(
89 *other_field,
90 "the other `#[xml(text)]` field is here",
91 ));
92 return Err(err);
93 }
94 text_field = Some(field.member().span())
95 }
96
97 fields.push(field);
98 }
99 Ok(Self {
100 fields,
101 unknown_attribute_policy,
102 unknown_child_policy,
103 })
104 }
105
106 pub(crate) fn from_fields(
108 compound_fields: &Fields,
109 container_namespace: &NamespaceRef,
110 unknown_attribute_policy: Option<Ident>,
111 unknown_child_policy: Option<Ident>,
112 ) -> Result<Self> {
113 Self::from_field_defs(
114 compound_fields.iter().enumerate().map(|(i, field)| {
115 let index = match i.try_into() {
116 Ok(v) => v,
117 Err(_) => {
120 return Err(Error::new_spanned(
121 field,
122 "okay, mate, that are way too many fields. get your life together.",
123 ))
124 }
125 };
126 FieldDef::from_field(field, index, container_namespace)
127 }),
128 unknown_attribute_policy,
129 unknown_child_policy,
130 )
131 }
132
133 pub(crate) fn make_from_events_statemachine(
139 &self,
140 state_ty_ident: &Ident,
141 output_name: &ParentRef,
142 state_prefix: &str,
143 ) -> Result<FromEventsSubmachine> {
144 let scope = FromEventsScope::new(state_ty_ident.clone());
145 let FromEventsScope {
146 ref attrs,
147 ref builder_data_ident,
148 ref text,
149 ref substate_data,
150 ref substate_result,
151 ..
152 } = scope;
153
154 let default_state_ident = quote::format_ident!("{}Default", state_prefix);
155 let discard_state_ident = quote::format_ident!("{}Discard", state_prefix);
156 let builder_data_ty: Type = TypePath {
157 qself: None,
158 path: quote::format_ident!("{}Data{}", state_ty_ident, state_prefix).into(),
159 }
160 .into();
161 let mut states = Vec::new();
162
163 let mut builder_data_def = TokenStream::default();
164 let mut builder_data_init = TokenStream::default();
165 let mut output_cons = TokenStream::default();
166 let mut child_matchers = TokenStream::default();
167 let mut fallback_child_matcher = None;
168 let mut text_handler = None;
169 let mut extra_defs = TokenStream::default();
170 let is_tuple = !output_name.is_path();
171
172 for (i, field) in self.fields.iter().enumerate() {
173 let member = field.member();
174 let builder_field_name = mangle_member(member);
175 let part = field.make_builder_part(&scope, output_name)?;
176 let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
177
178 match part {
179 FieldBuilderPart::Init {
180 value: FieldTempInit { ty, init },
181 } => {
182 builder_data_def.extend(quote! {
183 #builder_field_name: #ty,
184 });
185
186 builder_data_init.extend(quote! {
187 #builder_field_name: #init,
188 });
189
190 if is_tuple {
191 output_cons.extend(quote! {
192 #builder_data_ident.#builder_field_name,
193 });
194 } else {
195 output_cons.extend(quote! {
196 #member: #builder_data_ident.#builder_field_name,
197 });
198 }
199 }
200
201 FieldBuilderPart::Text {
202 value: FieldTempInit { ty, init },
203 collect,
204 finalize,
205 } => {
206 if text_handler.is_some() {
207 panic!("more than one field attempts to collect text data");
210 }
211
212 builder_data_def.extend(quote! {
213 #builder_field_name: #ty,
214 });
215 builder_data_init.extend(quote! {
216 #builder_field_name: #init,
217 });
218 text_handler = Some(quote! {
219 #collect
220 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
221 Self::#default_state_ident { #builder_data_ident }
222 ))
223 });
224
225 if is_tuple {
226 output_cons.extend(quote! {
227 #finalize,
228 });
229 } else {
230 output_cons.extend(quote! {
231 #member: #finalize,
232 });
233 }
234 }
235
236 FieldBuilderPart::Nested {
237 extra_defs: field_extra_defs,
238 value: FieldTempInit { ty, init },
239 matcher,
240 builder,
241 collect,
242 finalize,
243 } => {
244 let feed = feed_fn(builder.clone());
245
246 states.push(State::new_with_builder(
247 state_name.clone(),
248 &builder_data_ident,
249 &builder_data_ty,
250 ).with_field(
251 substate_data,
252 &builder,
253 ).with_mut(substate_data).with_impl(quote! {
254 match #feed(&mut #substate_data, ev)? {
255 ::core::option::Option::Some(#substate_result) => {
256 #collect
257 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
258 #builder_data_ident,
259 }))
260 }
261 ::core::option::Option::None => {
262 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
263 #builder_data_ident,
264 #substate_data,
265 }))
266 }
267 }
268 }));
269
270 builder_data_def.extend(quote! {
271 #builder_field_name: #ty,
272 });
273
274 builder_data_init.extend(quote! {
275 #builder_field_name: #init,
276 });
277
278 match matcher {
279 NestedMatcher::Selective(matcher) => {
280 child_matchers.extend(quote! {
281 let (name, attrs) = match #matcher {
282 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs }) => (name, attrs),
283 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(e)) => return ::core::result::Result::Err(e),
284 ::core::result::Result::Ok(#substate_data) => {
285 return ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
286 #builder_data_ident,
287 #substate_data,
288 }))
289 }
290 };
291 });
292 }
293 NestedMatcher::Fallback(matcher) => {
294 if let Some((span, _)) = fallback_child_matcher.as_ref() {
295 let mut err = Error::new(
296 field.span(),
297 "more than one field is attempting to consume all unmatched child elements"
298 );
299 err.combine(Error::new(
300 *span,
301 "the previous field collecting all unmatched child elements is here"
302 ));
303 return Err(err);
304 }
305
306 let matcher = quote! {
307 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#state_name {
308 #builder_data_ident,
309 #substate_data: { #matcher },
310 }))
311 };
312
313 fallback_child_matcher = Some((field.span(), matcher));
314 }
315 }
316
317 if is_tuple {
318 output_cons.extend(quote! {
319 #finalize,
320 });
321 } else {
322 output_cons.extend(quote! {
323 #member: #finalize,
324 });
325 }
326
327 extra_defs.extend(field_extra_defs);
328 }
329 }
330 }
331
332 let text_handler = match text_handler {
333 Some(v) => v,
334 None => quote! {
335 if !::xso::is_xml_whitespace(#text.as_bytes()) {
338 ::core::result::Result::Err(::xso::error::Error::Other("Unexpected text content".into()))
339 } else {
340 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
341 Self::#default_state_ident { #builder_data_ident }
342 ))
343 }
344 },
345 };
346
347 let unknown_attr_err = format!("Unknown attribute in {}.", output_name);
348 let unknown_child_err = format!("Unknown child in {}.", output_name);
349 let unknown_child_policy = &self.unknown_child_policy;
350
351 let output_cons = match output_name {
352 ParentRef::Named(ref path) => {
353 quote! {
354 #path { #output_cons }
355 }
356 }
357 ParentRef::Unnamed { .. } => {
358 quote! {
359 ( #output_cons )
360 }
361 }
362 };
363
364 let discard_builder_ty = discard_builder_ty(Span::call_site());
365 let discard_feed = feed_fn(discard_builder_ty.clone());
366 let child_fallback = match fallback_child_matcher {
367 Some((_, matcher)) => matcher,
368 None => quote! {
369 let _ = (name, attrs);
370 let _: () = #unknown_child_policy.apply_policy(#unknown_child_err)?;
371 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
372 #builder_data_ident,
373 #substate_data: #discard_builder_ty::new(),
374 }))
375 },
376 };
377
378 states.push(State::new_with_builder(
379 discard_state_ident.clone(),
380 &builder_data_ident,
381 &builder_data_ty,
382 ).with_field(
383 substate_data,
384 &discard_builder_ty,
385 ).with_mut(substate_data).with_impl(quote! {
386 match #discard_feed(&mut #substate_data, ev)? {
387 ::core::option::Option::Some(#substate_result) => {
388 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
389 #builder_data_ident,
390 }))
391 }
392 ::core::option::Option::None => {
393 ::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#discard_state_ident {
394 #builder_data_ident,
395 #substate_data,
396 }))
397 }
398 }
399 }));
400
401 states.push(State::new_with_builder(
402 default_state_ident.clone(),
403 builder_data_ident,
404 &builder_data_ty,
405 ).with_impl(quote! {
406 match ev {
407 ::xso::exports::rxml::Event::EndElement(_) => {
409 ::core::result::Result::Ok(::core::ops::ControlFlow::Continue(
410 #output_cons
411 ))
412 }
413 ::xso::exports::rxml::Event::StartElement(_, name, attrs) => {
414 #child_matchers
415 #child_fallback
416 }
417 ::xso::exports::rxml::Event::Text(_, #text) => {
418 #text_handler
419 }
420 ::xso::exports::rxml::Event::XmlDeclaration(_, ::xso::exports::rxml::XmlVersion::V1_0) => ::core::result::Result::Ok(::core::ops::ControlFlow::Break(
425 Self::#default_state_ident { #builder_data_ident }
426 ))
427 }
428 }));
429
430 let unknown_attribute_policy = &self.unknown_attribute_policy;
431
432 Ok(FromEventsSubmachine {
433 defs: quote! {
434 #extra_defs
435
436 struct #builder_data_ty {
437 #builder_data_def
438 }
439 },
440 states,
441 init: quote! {
442 let #builder_data_ident = #builder_data_ty {
443 #builder_data_init
444 };
445 if #attrs.len() > 0 {
446 let _: () = #unknown_attribute_policy.apply_policy(#unknown_attr_err)?;
447 }
448 ::core::result::Result::Ok(#state_ty_ident::#default_state_ident { #builder_data_ident })
449 },
450 })
451 }
452
453 pub(crate) fn make_as_item_iter_statemachine(
463 &self,
464 input_name: &ParentRef,
465 state_ty_ident: &Ident,
466 state_prefix: &str,
467 lifetime: &Lifetime,
468 ) -> Result<AsItemsSubmachine> {
469 let scope = AsItemsScope::new(lifetime, state_ty_ident.clone());
470
471 let element_head_start_state_ident =
472 quote::format_ident!("{}ElementHeadStart", state_prefix);
473 let element_head_end_state_ident = quote::format_ident!("{}ElementHeadEnd", state_prefix);
474 let element_foot_state_ident = quote::format_ident!("{}ElementFoot", state_prefix);
475 let name_ident = quote::format_ident!("name");
476 let ns_ident = quote::format_ident!("ns");
477 let dummy_ident = quote::format_ident!("dummy");
478 let mut states = Vec::new();
479
480 let is_tuple = !input_name.is_path();
481 let mut destructure = TokenStream::default();
482 let mut start_init = TokenStream::default();
483 let mut extra_defs = TokenStream::default();
484
485 states.push(
486 State::new(element_head_start_state_ident.clone())
487 .with_field(&dummy_ident, &phantom_lifetime_ty(lifetime.clone()))
488 .with_field(&ns_ident, &namespace_ty(Span::call_site()))
489 .with_field(
490 &name_ident,
491 &ncnamestr_cow_ty(Span::call_site(), lifetime.clone()),
492 ),
493 );
494
495 let mut element_head_end_idx = states.len();
496 states.push(
497 State::new(element_head_end_state_ident.clone()).with_impl(quote! {
498 ::core::option::Option::Some(::xso::Item::ElementHeadEnd)
499 }),
500 );
501
502 for (i, field) in self.fields.iter().enumerate() {
503 let member = field.member();
504 let bound_name = mangle_member(member);
505 let part = field.make_iterator_part(&scope, input_name, &bound_name)?;
506 let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
507 let ty = scope.borrow(field.ty().clone());
508
509 match part {
510 FieldIteratorPart::Header { generator } => {
511 for state in &mut states[..element_head_end_idx] {
514 state.add_field(&bound_name, &ty);
515 }
516 states.insert(
517 element_head_end_idx,
518 State::new(state_name)
519 .with_field(&bound_name, &ty)
520 .with_impl(quote! {
521 #generator
522 }),
523 );
524 element_head_end_idx += 1;
525
526 if is_tuple {
527 destructure.extend(quote! {
528 ref #bound_name,
529 });
530 } else {
531 destructure.extend(quote! {
532 #member: ref #bound_name,
533 });
534 }
535 start_init.extend(quote! {
536 #bound_name,
537 });
538 }
539
540 FieldIteratorPart::Text { generator } => {
541 for state in states.iter_mut() {
544 state.add_field(&bound_name, &ty);
545 }
546 states.push(
547 State::new(state_name)
548 .with_field(&bound_name, &ty)
549 .with_impl(quote! {
550 #generator.map(|value| ::xso::Item::Text(
551 value,
552 ))
553 }),
554 );
555 if is_tuple {
556 destructure.extend(quote! {
557 #bound_name,
558 });
559 } else {
560 destructure.extend(quote! {
561 #member: #bound_name,
562 });
563 }
564 start_init.extend(quote! {
565 #bound_name,
566 });
567 }
568
569 FieldIteratorPart::Content {
570 extra_defs: field_extra_defs,
571 value: FieldTempInit { ty, init },
572 generator,
573 } => {
574 for state in states.iter_mut() {
577 state.add_field(&bound_name, &ty);
578 }
579
580 states.push(
581 State::new(state_name.clone())
582 .with_field(&bound_name, &ty)
583 .with_mut(&bound_name)
584 .with_impl(quote! {
585 #generator?
586 }),
587 );
588 if is_tuple {
589 destructure.extend(quote! {
590 #bound_name,
591 });
592 } else {
593 destructure.extend(quote! {
594 #member: #bound_name,
595 });
596 }
597 start_init.extend(quote! {
598 #bound_name: #init,
599 });
600
601 extra_defs.extend(field_extra_defs);
602 }
603 }
604 }
605
606 states[0].set_impl(quote! {
607 {
608 ::core::option::Option::Some(::xso::Item::ElementHeadStart(
609 #ns_ident,
610 #name_ident,
611 ))
612 }
613 });
614
615 states.push(
616 State::new(element_foot_state_ident.clone()).with_impl(quote! {
617 ::core::option::Option::Some(::xso::Item::ElementFoot)
618 }),
619 );
620
621 let destructure = match input_name {
622 ParentRef::Named(ref input_path) => quote! {
623 #input_path { #destructure }
624 },
625 ParentRef::Unnamed { .. } => quote! {
626 ( #destructure )
627 },
628 };
629
630 Ok(AsItemsSubmachine {
631 defs: extra_defs,
632 states,
633 destructure,
634 init: quote! {
635 Self::#element_head_start_state_ident { #dummy_ident: ::core::marker::PhantomData, #name_ident: name.1, #ns_ident: name.0, #start_init }
636 },
637 })
638 }
639
640 pub(crate) fn single_ty(&self) -> Option<&Type> {
645 if self.fields.len() > 1 {
646 return None;
647 }
648 self.fields.get(0).map(|x| x.ty())
649 }
650
651 pub(crate) fn to_tuple_ty(&self) -> TypeTuple {
654 TypeTuple {
655 paren_token: token::Paren::default(),
656 elems: self.fields.iter().map(|x| x.ty().clone()).collect(),
657 }
658 }
659
660 pub(crate) fn to_single_or_tuple_ty(&self) -> Type {
663 match self.single_ty() {
664 None => self.to_tuple_ty().into(),
665 Some(v) => v.clone(),
666 }
667 }
668
669 pub(crate) fn to_ref_tuple_ty(&self, lifetime: &Lifetime) -> TypeTuple {
673 TypeTuple {
674 paren_token: token::Paren::default(),
675 elems: self
676 .fields
677 .iter()
678 .map(|x| ref_ty(x.ty().clone(), lifetime.clone()))
679 .collect(),
680 }
681 }
682
683 pub(crate) fn field_count(&self) -> usize {
685 self.fields.len()
686 }
687}