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