xso_proc/state.rs
1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7//! State machines for parsing and serialising of structs and enums.
8
9use proc_macro2::TokenStream;
10use quote::{quote, ToTokens};
11use syn::*;
12
13/// A single state in a parser or serializer state machine.
14#[derive(Clone)]
15pub(crate) struct State {
16 /// Name of the state enum variant for this state.
17 name: Ident,
18
19 /// Declaration of members of the state enum in this state.
20 decl: TokenStream,
21
22 /// Destructuring of members of the state enum in this state.
23 destructure: TokenStream,
24
25 /// Right-hand-side of the match arm for this state.
26 advance_body: TokenStream,
27
28 /// If set, that identifier will be bound mutably.
29 uses_mut: Option<Ident>,
30}
31
32impl State {
33 /// Create a new state with the a builder data field.
34 ///
35 /// This is a convenience wrapper around `new()` and `add_field()`. This
36 /// wrapper, or its equivalent, **must** be used for states used in
37 /// [`FromEventsStateMachine`] state machines, as those expect that the
38 /// first field is the builder data at render time.
39 pub(crate) fn new_with_builder(
40 name: Ident,
41 builder_data_ident: &Ident,
42 builder_data_ty: &Type,
43 ) -> Self {
44 let mut result = Self::new(name);
45 result.add_field(builder_data_ident, builder_data_ty);
46 result
47 }
48
49 /// Create a new, empty state.
50 ///
51 /// Note that an empty state will generate invalid code. At the very
52 /// least, a body must be added using [`Self::set_impl`] or
53 /// [`Self::with_impl`]. The various state machines may also have
54 /// additional requirements.
55 pub(crate) fn new(name: Ident) -> Self {
56 Self {
57 name,
58 decl: TokenStream::default(),
59 destructure: TokenStream::default(),
60 advance_body: TokenStream::default(),
61 uses_mut: None,
62 }
63 }
64
65 /// Add a field to this state's data.
66 ///
67 /// - `name` is the name under which the data will be accessible in the
68 /// state's implementation.
69 /// - `ty` must be the data field's type.
70 pub(crate) fn add_field(&mut self, name: &Ident, ty: &Type) {
71 self.decl.extend(quote! { #name: #ty, });
72 self.destructure.extend(quote! { #name, });
73 }
74
75 /// Modify the state to include another field and return the modified
76 /// state.
77 ///
78 /// This is a consume-and-return-style version of [`Self::add_field`].
79 pub(crate) fn with_field(mut self, name: &Ident, ty: &Type) -> Self {
80 self.add_field(name, ty);
81 self
82 }
83
84 /// Set the `advance` implementation of this state.
85 ///
86 /// `body` must be the body of the right hand side of the match arm for
87 /// the `advance` implementation of the state machine.
88 ///
89 /// See [`FromEventsStateMachine::advance_match_arms`] and
90 /// [`AsItemsSubmachine::compile`] for the respective
91 /// requirements on the implementations.
92 pub(crate) fn with_impl(mut self, body: TokenStream) -> Self {
93 self.advance_body = body;
94 self
95 }
96
97 /// Override the current `advance` implementation of this state.
98 ///
99 /// This is an in-place version of [`Self::with_impl`].
100 pub(crate) fn set_impl(&mut self, body: TokenStream) {
101 self.advance_body = body;
102 }
103
104 /// Modify the state to mark the given field as mutable and return the
105 /// modified state.
106 pub(crate) fn with_mut(mut self, ident: &Ident) -> Self {
107 assert!(self.uses_mut.is_none());
108 self.uses_mut = Some(ident.clone());
109 self
110 }
111}
112
113/// A partial [`FromEventsStateMachine`] which only covers the builder for a
114/// single compound.
115///
116/// See [`FromEventsStateMachine`] for more information on the state machines
117/// in general.
118pub(crate) struct FromEventsSubmachine {
119 /// Additional items necessary for the statemachine.
120 pub(crate) defs: TokenStream,
121
122 /// States and state transition implementations.
123 pub(crate) states: Vec<State>,
124
125 /// Initializer snippet.
126 ///
127 /// The structure needed here depends on the
128 /// [`FromEventsStateMachine::mode`] field of the final state machine this
129 /// submachine is compiled to or merged into.
130 pub(crate) init: TokenStream,
131}
132
133impl FromEventsSubmachine {
134 /// Convert a partial state machine into a full state machine.
135 ///
136 /// This converts the abstract [`State`] items into token
137 /// streams for the respective parts of the state machine (the state
138 /// definitions and the match arms), rendering them effectively immutable.
139 pub(crate) fn compile(self) -> FromEventsStateMachine {
140 let mut state_defs = TokenStream::default();
141 let mut advance_match_arms = TokenStream::default();
142
143 for state in self.states {
144 let State {
145 name,
146 decl,
147 destructure,
148 advance_body,
149 uses_mut,
150 } = state;
151
152 state_defs.extend(quote! {
153 #name { #decl },
154 });
155
156 let binding = if let Some(uses_mut) = uses_mut.as_ref() {
157 quote! {
158 let mut #uses_mut = #uses_mut;
159 }
160 } else {
161 TokenStream::default()
162 };
163
164 // XXX: nasty hack, but works: the first member of the enum always
165 // exists and it always is the builder data, which we always need
166 // mutably available. So we can just prefix the destructuring
167 // token stream with `mut` to make that first member mutable.
168 advance_match_arms.extend(quote! {
169 Self::#name { mut #destructure } => {
170 #binding
171 #advance_body
172 }
173 });
174 }
175
176 FromEventsStateMachine {
177 defs: self.defs,
178 state_defs,
179 advance_match_arms,
180 variants: vec![FromEventsEntryPoint { init: self.init }],
181 mode: FromEventsMatchMode::default(),
182 pre_init: TokenStream::default(),
183 fallback: None,
184 }
185 }
186
187 /// Update the [`init`][`Self::init`] field in-place.
188 ///
189 /// The function will receive a reference to the current `init` value,
190 /// allowing to create "wrappers" around that existing code.
191 pub(crate) fn with_augmented_init<F: FnOnce(&TokenStream) -> TokenStream>(
192 mut self,
193 f: F,
194 ) -> Self {
195 let new_init = f(&self.init);
196 self.init = new_init;
197 self
198 }
199}
200
201/// A partial [`AsItemsStateMachine`] which only covers the builder for a
202/// single compound.
203///
204/// See [`AsItemsStateMachine`] for more information on the state machines
205/// in general.
206pub(crate) struct AsItemsSubmachine {
207 /// Additional items necessary for the statemachine.
208 pub(crate) defs: TokenStream,
209
210 /// States and state transition implementations.
211 pub(crate) states: Vec<State>,
212
213 /// A pattern match which destructures the target type into its parts, for
214 /// use by `init`.
215 pub(crate) destructure: TokenStream,
216
217 /// An expression which uses the names bound in `destructure` to create a
218 /// an instance of the state enum.
219 ///
220 /// The state enum type is available as `Self` in that context.
221 pub(crate) init: TokenStream,
222}
223
224impl AsItemsSubmachine {
225 /// Convert a partial state machine into a full state machine.
226 ///
227 /// This converts the abstract [`State`] items into token
228 /// streams for the respective parts of the state machine (the state
229 /// definitions and the match arms), rendering them effectively immutable.
230 ///
231 /// This requires that the [`State::advance_body`] token streams evaluate
232 /// to an `Option<Item>`. If it evaluates to `Some(.)`, that is
233 /// emitted from the iterator. If it evaluates to `None`, the `advance`
234 /// implementation is called again.
235 ///
236 /// Each state implementation is augmented to also enter the next state,
237 /// causing the iterator to terminate eventually.
238 pub(crate) fn compile(self) -> AsItemsStateMachine {
239 let mut state_defs = TokenStream::default();
240 let mut advance_match_arms = TokenStream::default();
241
242 for (i, state) in self.states.iter().enumerate() {
243 let State {
244 ref name,
245 ref decl,
246 ref destructure,
247 ref advance_body,
248 ref uses_mut,
249 } = state;
250
251 let footer = match self.states.get(i + 1) {
252 Some(State {
253 name: ref next_name,
254 destructure: ref construct_next,
255 ..
256 }) => {
257 quote! {
258 ::core::result::Result::Ok((::core::option::Option::Some(Self::#next_name { #construct_next }), item))
259 }
260 }
261 // final state -> exit the state machine
262 None => {
263 quote! {
264 ::core::result::Result::Ok((::core::option::Option::None, item))
265 }
266 }
267 };
268
269 state_defs.extend(quote! {
270 #name { #decl },
271 });
272
273 if let Some(uses_mut) = uses_mut.as_ref() {
274 // the variant is non-consuming, meaning it can be called
275 // multiple times and it uses the identifier in `uses_mut`
276 // mutably.
277 // the transition is only triggered when it emits a None
278 // item
279 // (we cannot do this at the place the `State` is constructed,
280 // because we don't yet know all its fields then; it must be
281 // done here.)
282 advance_match_arms.extend(quote! {
283 Self::#name { #destructure } => {
284 let mut #uses_mut = #uses_mut;
285 match #advance_body {
286 ::core::option::Option::Some(item) => {
287 ::core::result::Result::Ok((::core::option::Option::Some(Self::#name { #destructure }), ::core::option::Option::Some(item)))
288 },
289 item => { #footer },
290 }
291 }
292 });
293 } else {
294 // if the variant is consuming, it can only be called once.
295 // it may or may not emit an event, but the transition is
296 // always triggered
297 advance_match_arms.extend(quote! {
298 Self::#name { #destructure } => {
299 let item = #advance_body;
300 #footer
301 }
302 });
303 }
304 }
305
306 AsItemsStateMachine {
307 defs: self.defs,
308 state_defs,
309 advance_match_arms,
310 variants: vec![AsItemsEntryPoint {
311 init: self.init,
312 destructure: self.destructure,
313 }],
314 }
315 }
316
317 /// Insert a state into the element header state chain.
318 ///
319 /// It is not possible to add, remove or access a field from within this
320 /// state; all data from the first state will be passed through this state
321 /// unaltered.
322 ///
323 /// `name` must be the unambiguous name of the state.
324 ///
325 /// `item` must be an expression which generates the `Item` to be emitted
326 /// from this state.
327 pub(crate) fn with_extra_header_state(mut self, name: Ident, item: TokenStream) -> Self {
328 // We always have at least three states: ElementHeadStart,
329 // ElementHeadEnd, ElementFoot.
330 assert!(self.states.len() >= 3);
331
332 // We then use the first state after the ElementHeadStart as template.
333 // It does not really matter which one we use, as long as we do not
334 // use ElementHeadStart (because that would be before the element
335 // header) or any state after the ElementHeadEnd.
336 let mut template = self.states[1].clone();
337 // Override all fields but decl and destructure. Those define the data
338 // which is carried by the State, and we must keep that intact to pass
339 // the data through the state machine unaltered.
340 template.name = name;
341 template.uses_mut = None;
342 template.advance_body = quote! { ::core::option::Option::Some(#item) };
343
344 self.states.insert(1, template);
345
346 self
347 }
348
349 /// Update the [`init`][`Self::init`] field in-place.
350 ///
351 /// The function will receive a reference to the current `init` value,
352 /// allowing to create "wrappers" around that existing code.
353 pub(crate) fn with_augmented_init<F: FnOnce(&TokenStream) -> TokenStream>(
354 mut self,
355 f: F,
356 ) -> Self {
357 let new_init = f(&self.init);
358 self.init = new_init;
359 self
360 }
361}
362
363/// Container for a single entrypoint into a [`FromEventsStateMachine`].
364pub(crate) struct FromEventsEntryPoint {
365 pub(crate) init: TokenStream,
366}
367
368/// A single variant's entrypoint into the event iterator.
369pub(crate) struct AsItemsEntryPoint {
370 /// A pattern match which destructures the target type into its parts, for
371 /// use by `init`.
372 destructure: TokenStream,
373
374 /// An expression which uses the names bound in `destructure` to create a
375 /// an instance of the state enum.
376 ///
377 /// The state enum type is available as `Self` in that context.
378 init: TokenStream,
379}
380
381/// Control how a state machine attempts to match elements.
382#[derive(Default)]
383pub(crate) enum FromEventsMatchMode {
384 /// Element matching works by chaining the individual submachine's init
385 /// code.
386 ///
387 /// Each submachine has to provide an expression which evaluates to a
388 /// `Result<#state_ty_ident, xso::FromEventsError>` in its
389 /// [`FromEventsSubmachine::init`] code.
390 ///
391 /// If the expression evaluates to
392 /// `Err(FromEventsError::Mismatch { .. })`, the next submachine in merge
393 /// order is tried. If no further submachines exist, the
394 /// [`fallback`][`FromEventsStateMachine::fallback`] code
395 /// is injected (or the mismatch is returned if no fallback code exists.)
396 ///
397 /// This is the default mode.
398 #[default]
399 Chained,
400
401 /// Element matching works with a `match .. { .. }` block.
402 ///
403 /// Each submachine has to provide a match arm (`pattern => result,`)
404 /// snippet in its [`FromEventsSubmachine::init`] code. The `result` must
405 /// evaluate to a `Result<#state_ty_ident, xso::FromEventsError>`.
406 ///
407 /// The statemachine will generate a final arm (`_ => #fallback,`) using
408 /// the [`fallback`][`FromEventsStateMachine::fallback`] code
409 /// if it exists, and a normal `Mismatch` error otherwise.
410 ///
411 /// The `pattern` must be compatible to the `expr` provided here.
412 Matched {
413 /// Expression which the arms match against.
414 expr: TokenStream,
415 },
416}
417
418/// # State machine to implement `xso::FromEventsBuilder`
419///
420/// This struct represents a state machine consisting of the following parts:
421///
422/// - Extra dependencies ([`Self::defs`])
423/// - States ([`Self::state_defs`])
424/// - Transitions ([`Self::advance_match_arms`])
425/// - Entrypoints ([`Self::variants`])
426///
427/// Such a state machine is best constructed by constructing one or
428/// more [`FromEventsSubmachine`] structs and converting/merging them using
429/// `into()` and [`merge`][`Self::merge`].
430///
431/// A state machine has an output type (corresponding to
432/// `xso::FromEventsBuilder::Output`), which is however only implicitly defined
433/// by the expressions generated in the `advance_match_arms`. That means that
434/// merging submachines with different output types works, but will then generate
435/// code which will fail to compile.
436///
437/// When converted to Rust code, the state machine will manifest as (among other
438/// things) an enum type which contains all states and which has an `advance`
439/// method. That method consumes the enum value and returns either a new enum
440/// value, an error, or the output type of the state machine.
441#[derive(Default)]
442pub(crate) struct FromEventsStateMachine {
443 /// Extra items which are needed for the state machine implementation.
444 defs: TokenStream,
445
446 /// Extra code run during pre-init phase.
447 pre_init: TokenStream,
448
449 /// Code to run as fallback if none of the branches matched the start
450 /// event.
451 ///
452 /// If absent, a `FromEventsError::Mismatch` is generated.
453 fallback: Option<TokenStream>,
454
455 /// Configure how the element is matched.
456 ///
457 /// This controls the syntax required in each submachine's
458 /// [`FromEventsSubmachine::init`]. See [`FromEventsMatchMode`] for
459 /// details.
460 mode: FromEventsMatchMode,
461
462 /// A sequence of enum variant declarations, separated and terminated by
463 /// commas.
464 state_defs: TokenStream,
465
466 /// A sequence of `match self { .. }` arms, where `self` is the state
467 /// enumeration type.
468 ///
469 /// Each match arm must either diverge or evaluate to a
470 /// `Result<ControlFlow<State, Output>, xso::error::Error>`, where `State`
471 /// is the state enumeration and `Output` is the state machine's output
472 /// type.
473 advance_match_arms: TokenStream,
474
475 /// The different entrypoints for the state machine.
476 ///
477 /// This may only contain more than one element if an enumeration is being
478 /// constructed by the resulting state machine.
479 variants: Vec<FromEventsEntryPoint>,
480}
481
482impl FromEventsStateMachine {
483 /// Create a new, empty state machine.
484 pub(crate) fn new() -> Self {
485 Self {
486 defs: TokenStream::default(),
487 state_defs: TokenStream::default(),
488 advance_match_arms: TokenStream::default(),
489 pre_init: TokenStream::default(),
490 fallback: None,
491 mode: FromEventsMatchMode::default(),
492 variants: Vec::new(),
493 }
494 }
495
496 /// Merge another state machine into this state machine.
497 ///
498 /// This *discards* the other state machine's pre-init code.
499 pub(crate) fn merge(&mut self, other: FromEventsStateMachine) {
500 assert!(other.fallback.is_none());
501 self.defs.extend(other.defs);
502 self.state_defs.extend(other.state_defs);
503 self.advance_match_arms.extend(other.advance_match_arms);
504 self.variants.extend(other.variants);
505 }
506
507 /// Set additional code to inject at the head of the `new` method for the
508 /// builder.
509 ///
510 /// This can be used to do preliminary checks and is commonly used with
511 /// specifically-formed init codes on the variants.
512 pub(crate) fn set_pre_init(&mut self, code: TokenStream) {
513 self.pre_init = code;
514 }
515
516 /// Set the fallback code to use if none of the branches matches the start
517 /// event.
518 ///
519 /// By default, a `FromEventsError::Mismatch` is generated.
520 pub(crate) fn set_fallback(&mut self, code: TokenStream) {
521 self.fallback = Some(code);
522 }
523
524 /// Set the matching mode for the rendered state machine.
525 ///
526 /// See [`FromEventsMatchMode`] for details.
527 pub(crate) fn set_match_mode(&mut self, mode: FromEventsMatchMode) {
528 self.mode = mode;
529 }
530
531 /// Render the state machine as a token stream.
532 ///
533 /// The token stream contains the following pieces:
534 /// - Any definitions necessary for the statemachine to operate
535 /// - The state enum
536 /// - The builder struct
537 /// - The `xso::FromEventsBuilder` impl on the builder struct
538 /// - A `fn new(rxml::QName, rxml::AttrMap) -> Result<Self>` on the
539 /// builder struct.
540 pub(crate) fn render(
541 self,
542 vis: &Visibility,
543 builder_ty_ident: &Ident,
544 state_ty_ident: &Ident,
545 output_ty: &Type,
546 validate_fn: Option<&Path>,
547 ) -> Result<TokenStream> {
548 let Self {
549 defs,
550 state_defs,
551 advance_match_arms,
552 variants,
553 pre_init,
554 fallback,
555 mode,
556 } = self;
557
558 let output_ty_ref = make_ty_ref(output_ty);
559
560 let docstr = format!("Build a {0} from XML events.\n\nThis type is generated using the [`macro@xso::FromXml`] derive macro and implements [`xso::FromEventsBuilder`] for {0}.", output_ty_ref);
561
562 let validate_call = match validate_fn {
563 None => quote! {
564 // needed to avoid unused_mut warning.
565 let _ = &mut value;
566 },
567 Some(validate_fn) => {
568 quote! {
569 #validate_fn(&mut value)?;
570 }
571 }
572 };
573
574 let fallback = fallback.unwrap_or_else(|| {
575 quote! {
576 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs })
577 }
578 });
579
580 let new_body = match mode {
581 FromEventsMatchMode::Chained => {
582 let mut init_body = pre_init;
583 for variant in variants {
584 let FromEventsEntryPoint { init } = variant;
585 init_body.extend(quote! {
586 let (name, mut attrs) = match { { let _ = &mut attrs; } #init } {
587 ::core::result::Result::Ok(v) => return ::core::result::Result::Ok(v),
588 ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(e)) => return ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(e)),
589 ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs }) => (name, attrs),
590 };
591 })
592 }
593
594 quote! {
595 #init_body
596 { let _ = &mut attrs; }
597 #fallback
598 }
599 }
600
601 FromEventsMatchMode::Matched { expr } => {
602 let mut match_body = TokenStream::default();
603 for variant in variants {
604 let FromEventsEntryPoint { init } = variant;
605 match_body.extend(init);
606 }
607
608 quote! {
609 #pre_init
610 match #expr {
611 #match_body
612 _ => #fallback,
613 }
614 }
615 }
616 };
617
618 Ok(quote! {
619 #defs
620
621 enum #state_ty_ident {
622 #state_defs
623 }
624
625 impl #state_ty_ident {
626 fn advance(mut self, ev: ::xso::exports::rxml::Event, ctx: &::xso::Context<'_>) -> ::core::result::Result<::core::ops::ControlFlow<Self, #output_ty>, ::xso::error::Error> {
627 match self {
628 #advance_match_arms
629 }.and_then(|__ok| {
630 match __ok {
631 ::core::ops::ControlFlow::Break(st) => ::core::result::Result::Ok(::core::ops::ControlFlow::Break(st)),
632 ::core::ops::ControlFlow::Continue(result) => {
633 ::core::result::Result::Ok(::core::ops::ControlFlow::Continue(result))
634 }
635 }
636 })
637 }
638 }
639
640 impl #builder_ty_ident {
641 fn new(
642 name: ::xso::exports::rxml::QName,
643 attrs: ::xso::exports::rxml::AttrMap,
644 ctx: &::xso::Context<'_>,
645 ) -> ::core::result::Result<Self, ::xso::error::FromEventsError> {
646 #state_ty_ident::new(name, attrs, ctx).map(|ok| Self(::core::option::Option::Some(ok)))
647 }
648 }
649
650 #[doc = #docstr]
651 #[doc(hidden)]
652 #vis struct #builder_ty_ident(::core::option::Option<#state_ty_ident>);
653
654 impl ::xso::FromEventsBuilder for #builder_ty_ident {
655 type Output = #output_ty;
656
657 fn feed(&mut self, ev: ::xso::exports::rxml::Event, ctx: &::xso::Context<'_>) -> ::core::result::Result<::core::option::Option<Self::Output>, ::xso::error::Error> {
658 let inner = self.0.take().expect("feed called after completion");
659 match inner.advance(ev, ctx)? {
660 ::core::ops::ControlFlow::Continue(mut value) => {
661 #validate_call
662 ::core::result::Result::Ok(::core::option::Option::Some(value))
663 }
664 ::core::ops::ControlFlow::Break(st) => {
665 self.0 = ::core::option::Option::Some(st);
666 ::core::result::Result::Ok(::core::option::Option::None)
667 }
668 }
669 }
670 }
671
672 impl #state_ty_ident {
673 fn new(
674 name: ::xso::exports::rxml::QName,
675 mut attrs: ::xso::exports::rxml::AttrMap,
676 ctx: &::xso::Context<'_>,
677 ) -> ::core::result::Result<Self, ::xso::error::FromEventsError> {
678 #new_body
679 }
680 }
681 })
682 }
683}
684
685/// # State machine to implement an `Iterator<Item = rxml::Event>`.
686///
687/// This struct represents a state machine consisting of the following parts:
688///
689/// - Extra dependencies ([`Self::defs`])
690/// - States ([`Self::state_defs`])
691/// - Transitions ([`Self::advance_match_arms`])
692/// - Entrypoints ([`Self::variants`])
693///
694/// Such a state machine is best constructed by constructing one or
695/// more [`FromEventsSubmachine`] structs and converting/merging them using
696/// `into()` and [`merge`][`Self::merge`].
697///
698/// A state machine has an output type (corresponding to
699/// `xso::FromEventsBuilder::Output`), which is however only implicitly defined
700/// by the expressions generated in the `advance_match_arms`. That means that
701/// merging submachines with different output types works, but will then generate
702/// code which will fail to compile.
703///
704/// When converted to Rust code, the state machine will manifest as (among other
705/// things) an enum type which contains all states and which has an `advance`
706/// method. That method consumes the enum value and returns either a new enum
707/// value, an error, or the output type of the state machine.
708#[derive(Default)]
709pub(crate) struct AsItemsStateMachine {
710 /// Extra items which are needed for the state machine implementation.
711 defs: TokenStream,
712
713 /// A sequence of enum variant declarations, separated and terminated by
714 /// commas.
715 state_defs: TokenStream,
716
717 /// A sequence of `match self { .. }` arms, where `self` is the state
718 /// enumeration type.
719 ///
720 /// Each match arm must either diverge or evaluate to a
721 /// `Result<(Option<State>, Option<Item>), xso::error::Error>`, where
722 /// where `State` is the state enumeration.
723 ///
724 /// If `Some(.)` is returned for the event, that event is emitted. If
725 /// `None` is returned for the event, the advance implementation is called
726 /// again after switching to the state returned in the `Option<State>`
727 /// field.
728 ///
729 /// If `None` is returned for the `Option<State>`, the iterator
730 /// terminates yielding the `Option<Item>` value directly (even if it is
731 /// `None`). After the iterator has terminated, it yields `None`
732 /// indefinitely.
733 advance_match_arms: TokenStream,
734
735 /// The different entrypoints for the state machine.
736 ///
737 /// This may only contain more than one element if an enumeration is being
738 /// serialised by the resulting state machine.
739 variants: Vec<AsItemsEntryPoint>,
740}
741
742impl AsItemsStateMachine {
743 /// Create a new, empty state machine.
744 pub(crate) fn new() -> Self {
745 Self {
746 defs: TokenStream::default(),
747 state_defs: TokenStream::default(),
748 advance_match_arms: TokenStream::default(),
749 variants: Vec::new(),
750 }
751 }
752
753 /// Merge another state machine into this state machine.
754 pub(crate) fn merge(&mut self, other: AsItemsStateMachine) {
755 self.defs.extend(other.defs);
756 self.state_defs.extend(other.state_defs);
757 self.advance_match_arms.extend(other.advance_match_arms);
758 self.variants.extend(other.variants);
759 }
760
761 /// Render the state machine as a token stream.
762 ///
763 /// The token stream contains the following pieces:
764 /// - Any definitions necessary for the statemachine to operate
765 /// - The state enum
766 /// - The iterator struct
767 /// - The `Iterator` impl on the builder struct
768 /// - A `fn new(T) -> Result<Self>` on the iterator struct.
769 pub(crate) fn render(
770 self,
771 vis: &Visibility,
772 input_ty_ref: &Type,
773 state_ty_ident: &Ident,
774 item_iter_ty_lifetime: &Lifetime,
775 item_iter_ty: &Type,
776 ) -> Result<TokenStream> {
777 let Self {
778 defs,
779 state_defs,
780 advance_match_arms,
781 mut variants,
782 } = self;
783
784 let input_ty_ref_text = make_ty_ref(input_ty_ref);
785 let docstr = format!("Convert a {0} into XML events.\n\nThis type is generated using the [`macro@xso::AsXml`] derive macro and implements [`core::iter:Iterator`] for {0}.", input_ty_ref_text);
786
787 let init_body = if variants.len() == 1 {
788 let AsItemsEntryPoint { destructure, init } = variants.remove(0);
789 quote! {
790 {
791 let #destructure = value;
792 #init
793 }
794 }
795 } else {
796 let mut match_arms = TokenStream::default();
797 for AsItemsEntryPoint { destructure, init } in variants {
798 match_arms.extend(quote! {
799 #destructure => { #init }
800 });
801 }
802
803 quote! {
804 match value {
805 #match_arms
806 }
807 }
808 };
809
810 Ok(quote! {
811 #defs
812
813 enum #state_ty_ident<#item_iter_ty_lifetime> {
814 #state_defs
815 }
816
817 impl<#item_iter_ty_lifetime> #state_ty_ident<#item_iter_ty_lifetime> {
818 fn advance(mut self) -> ::core::result::Result<(::core::option::Option<Self>, ::core::option::Option<::xso::Item<#item_iter_ty_lifetime>>), ::xso::error::Error> {
819 match self {
820 #advance_match_arms
821 }
822 }
823
824 fn new(
825 value: #input_ty_ref,
826 ) -> ::core::result::Result<Self, ::xso::error::Error> {
827 ::core::result::Result::Ok(#init_body)
828 }
829 }
830
831 #[doc = #docstr]
832 #[doc(hidden)]
833 #vis struct #item_iter_ty(::core::option::Option<#state_ty_ident<#item_iter_ty_lifetime>>);
834
835 impl<#item_iter_ty_lifetime> ::core::iter::Iterator for #item_iter_ty {
836 type Item = ::core::result::Result<::xso::Item<#item_iter_ty_lifetime>, ::xso::error::Error>;
837
838 fn next(&mut self) -> ::core::option::Option<Self::Item> {
839 let mut state = self.0.take()?;
840 loop {
841 let (next_state, item) = match state.advance() {
842 ::core::result::Result::Ok(v) => v,
843 ::core::result::Result::Err(e) => return ::core::option::Option::Some(::core::result::Result::Err(e)),
844 };
845 if let ::core::option::Option::Some(item) = item {
846 self.0 = next_state;
847 return ::core::option::Option::Some(::core::result::Result::Ok(item));
848 }
849 // no event, do we have a state?
850 if let ::core::option::Option::Some(st) = next_state {
851 // we do: try again!
852 state = st;
853 continue;
854 } else {
855 // we don't: end of iterator!
856 self.0 = ::core::option::Option::None;
857 return ::core::option::Option::None;
858 }
859 }
860 }
861 }
862
863 impl<#item_iter_ty_lifetime> #item_iter_ty {
864 fn new(value: #input_ty_ref) -> ::core::result::Result<Self, ::xso::error::Error> {
865 #state_ty_ident::new(value).map(|ok| Self(::core::option::Option::Some(ok)))
866 }
867 }
868 })
869 }
870}
871
872/// Construct a path for an intradoc link from a given type.
873fn doc_link_path(ty: &Type) -> Option<String> {
874 match ty {
875 Type::Path(ref ty) => {
876 let (mut buf, offset) = match ty.qself {
877 Some(ref qself) => {
878 let mut buf = doc_link_path(&qself.ty)?;
879 buf.push_str("::");
880 (buf, qself.position)
881 }
882 None => {
883 let mut buf = String::new();
884 if ty.path.leading_colon.is_some() {
885 buf.push_str("::");
886 }
887 (buf, 0)
888 }
889 };
890 let last = ty.path.segments.len() - 1;
891 for i in offset..ty.path.segments.len() {
892 let segment = &ty.path.segments[i];
893 buf.push_str(&segment.ident.to_string());
894 if i < last {
895 buf.push_str("::");
896 }
897 }
898 Some(buf)
899 }
900 Type::Reference(TypeReference { ref elem, .. }) => doc_link_path(elem),
901 _ => None,
902 }
903}
904
905/// Create a markdown snippet which references the given type as cleanly as
906/// possible.
907///
908/// This is used in documentation generation functions.
909///
910/// Not all types can be linked to; those which cannot be linked to will
911/// simply be wrapped in backticks.
912fn make_ty_ref(ty: &Type) -> String {
913 match doc_link_path(ty) {
914 Some(mut path) => {
915 path.reserve(4);
916 path.insert_str(0, "[`");
917 path.push_str("`]");
918 path
919 }
920 None => format!("`{}`", ty.to_token_stream()),
921 }
922}