Skip to main content

template_quote/
lib.rs

1#![doc(html_logo_url = "https://raw.githubusercontent.com/yasuo-ozu/template_quote/main/logo.png")]
2#![doc = include_str!("../README.md")]
3
4/// See module-level doc.
5pub use template_quote_impl::quote;
6/// [`quote_configured!`] is a configurable version of [`quote!`]. A leading
7/// `{ key: value, ... } =>` block overrides the paths and span the generated
8/// code refers to. Every key is optional; the accepted keys and their defaults
9/// are:
10///
11/// - `proc_macro2` — path to the `proc-macro2` crate (default `::proc_macro2`)
12/// - `quote` — path to this crate's re-exports (default `::template_quote`)
13/// - `core` — path to `core` (default `::core`)
14/// - `span` — an expression giving the span for emitted tokens (default
15///   `::proc_macro2::Span::call_site()`)
16///
17/// Any other key is a compile error.
18///
19/// ```ignore
20/// # use template_quote::quote_configured;
21/// quote_configured! {
22///     {
23///         proc_macro2: ::proc_macro2,
24///         quote: ::template_quote,
25///         core: ::core,
26///         span: ::proc_macro2::Span::call_site(),
27///     } =>
28///     // ... template body ...
29/// };
30/// ```
31pub use template_quote_impl::quote_configured;
32
33/// [`quote_spanned!`] emits tokens using the span given before `=>`.
34///
35/// ```ignore
36/// # use template_quote::quote_spanned;
37/// use proc_macro2::Span;
38/// let span = Span::call_site();
39/// let tokens = quote_spanned! { span => /* ... */ };
40/// ```
41pub use template_quote_impl::quote_spanned;
42
43pub use quote::ToTokens;
44
45/// Hidden runtime support referenced by the code generated by [`quote!`] and
46/// friends via `#path_quote::__private::…`. Not part of the public API.
47#[doc(hidden)]
48pub mod __private {
49	pub use crate::imp::ext;
50}
51
52mod imp {
53	/// Extension traits used by the generated repetition code to turn each
54	/// interpolated variable into an iterator.
55	///
56	/// Modeled on `quote`'s autoref-specialization: each tier exposes the same
57	/// method `__template_quote_into_iter`, returning `(iterator, is_iterable)`
58	/// in one call. The generated code binds `let (mut it, m) = (var).…();` per
59	/// variable, zips the iterators, ORs the `m` flags and panics at runtime if
60	/// none are iterable.
61	///
62	/// Splitting across three traits (instead of one trait with a blanket
63	/// `Borrow<[T]>` impl) is what removes the old coherence collision: a type
64	/// implementing both `Borrow<[T]>` and `ToTokens` used to match two impls
65	/// and fail with `E0283`. Here the "iterate by reference" tier is
66	/// implemented for concrete std types only, so by the orphan rule a foreign
67	/// type can never be both that tier and `ToTokens` — no ambiguity.
68	pub mod ext {
69		use quote::ToTokens;
70		use std::collections::btree_set::{self, BTreeSet};
71		use std::slice;
72
73		/// Tier 1 — real iterators, taken by value (wins method resolution with
74		/// no autoref). Reports `is_iterable = true`.
75		pub trait RepIteratorExt: Iterator + Sized {
76			fn __template_quote_into_iter(self) -> (Self, bool) {
77				(self, true)
78			}
79		}
80
81		impl<T: Iterator> RepIteratorExt for T {}
82
83		/// Tier 2 — plain [`ToTokens`] values, repeated unchanged each
84		/// iteration. `&self` receiver, so the value is never consumed.
85		/// Reports `is_iterable = false`.
86		pub trait RepToTokensExt {
87			fn __template_quote_into_iter(&self) -> (ToTokensRepeat<'_, Self>, bool) {
88				(ToTokensRepeat(self), false)
89			}
90		}
91
92		impl<T: ToTokens + ?Sized> RepToTokensExt for T {}
93
94		/// Infinite iterator yielding the same `&T` every step.
95		pub struct ToTokensRepeat<'a, T: ?Sized>(&'a T);
96
97		impl<'a, T: ?Sized> Iterator for ToTokensRepeat<'a, T> {
98			type Item = &'a T;
99			fn next(&mut self) -> Option<Self::Item> {
100				Some(self.0)
101			}
102		}
103
104		/// Tier 3 — "iterate by reference" for concrete std container and
105		/// reference types only (never a blanket over a user trait, so it can
106		/// never coherence-collide with [`RepToTokensExt`]). `&self` receiver;
107		/// reports `is_iterable = true`.
108		pub trait RepAsIteratorExt<'q> {
109			type Iter: Iterator;
110			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool);
111		}
112
113		impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T {
114			type Iter = T::Iter;
115			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
116				<T as RepAsIteratorExt>::__template_quote_into_iter(*self)
117			}
118		}
119
120		impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &mut T {
121			type Iter = T::Iter;
122			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
123				<T as RepAsIteratorExt>::__template_quote_into_iter(*self)
124			}
125		}
126
127		impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] {
128			type Iter = slice::Iter<'q, T>;
129			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
130				(self.iter(), true)
131			}
132		}
133
134		impl<'q, T: 'q, const N: usize> RepAsIteratorExt<'q> for [T; N] {
135			type Iter = slice::Iter<'q, T>;
136			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
137				(self.iter(), true)
138			}
139		}
140
141		impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec<T> {
142			type Iter = slice::Iter<'q, T>;
143			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
144				(self.iter(), true)
145			}
146		}
147
148		impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet<T> {
149			type Iter = btree_set::Iter<'q, T>;
150			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
151				(self.iter(), true)
152			}
153		}
154	}
155}