template-quote 0.5.0

A new-fashioned quote! macro implementation with pretty template-engine-like syntax
Documentation
#![doc(html_logo_url = "https://raw.githubusercontent.com/yasuo-ozu/template_quote/main/logo.png")]
#![doc = include_str!("../README.md")]

/// See module-level doc.
pub use template_quote_impl::quote;
/// [`quote_configured!`] is a configurable version of [`quote!`]. A leading
/// `{ key: value, ... } =>` block overrides the paths and span the generated
/// code refers to. Every key is optional; the accepted keys and their defaults
/// are:
///
/// - `proc_macro2` — path to the `proc-macro2` crate (default `::proc_macro2`)
/// - `quote` — path to this crate's re-exports (default `::template_quote`)
/// - `core` — path to `core` (default `::core`)
/// - `span` — an expression giving the span for emitted tokens (default
///   `::proc_macro2::Span::call_site()`)
///
/// Any other key is a compile error.
///
/// ```ignore
/// # use template_quote::quote_configured;
/// quote_configured! {
///     {
///         proc_macro2: ::proc_macro2,
///         quote: ::template_quote,
///         core: ::core,
///         span: ::proc_macro2::Span::call_site(),
///     } =>
///     // ... template body ...
/// };
/// ```
pub use template_quote_impl::quote_configured;

/// [`quote_spanned!`] emits tokens using the span given before `=>`.
///
/// ```ignore
/// # use template_quote::quote_spanned;
/// use proc_macro2::Span;
/// let span = Span::call_site();
/// let tokens = quote_spanned! { span => /* ... */ };
/// ```
pub use template_quote_impl::quote_spanned;

pub use quote::ToTokens;

/// Hidden runtime support referenced by the code generated by [`quote!`] and
/// friends via `#path_quote::__private::…`. Not part of the public API.
#[doc(hidden)]
pub mod __private {
	pub use crate::imp::ext;
}

mod imp {
	/// Extension traits used by the generated repetition code to turn each
	/// interpolated variable into an iterator.
	///
	/// Modeled on `quote`'s autoref-specialization: each tier exposes the same
	/// method `__template_quote_into_iter`, returning `(iterator, is_iterable)`
	/// in one call. The generated code binds `let (mut it, m) = (var).…();` per
	/// variable, zips the iterators, ORs the `m` flags and panics at runtime if
	/// none are iterable.
	///
	/// Splitting across three traits (instead of one trait with a blanket
	/// `Borrow<[T]>` impl) is what removes the old coherence collision: a type
	/// implementing both `Borrow<[T]>` and `ToTokens` used to match two impls
	/// and fail with `E0283`. Here the "iterate by reference" tier is
	/// implemented for concrete std types only, so by the orphan rule a foreign
	/// type can never be both that tier and `ToTokens` — no ambiguity.
	pub mod ext {
		use quote::ToTokens;
		use std::collections::btree_set::{self, BTreeSet};
		use std::slice;

		/// Tier 1 — real iterators, taken by value (wins method resolution with
		/// no autoref). Reports `is_iterable = true`.
		pub trait RepIteratorExt: Iterator + Sized {
			fn __template_quote_into_iter(self) -> (Self, bool) {
				(self, true)
			}
		}

		impl<T: Iterator> RepIteratorExt for T {}

		/// Tier 2 — plain [`ToTokens`] values, repeated unchanged each
		/// iteration. `&self` receiver, so the value is never consumed.
		/// Reports `is_iterable = false`.
		pub trait RepToTokensExt {
			fn __template_quote_into_iter(&self) -> (ToTokensRepeat<'_, Self>, bool) {
				(ToTokensRepeat(self), false)
			}
		}

		impl<T: ToTokens + ?Sized> RepToTokensExt for T {}

		/// Infinite iterator yielding the same `&T` every step.
		pub struct ToTokensRepeat<'a, T: ?Sized>(&'a T);

		impl<'a, T: ?Sized> Iterator for ToTokensRepeat<'a, T> {
			type Item = &'a T;
			fn next(&mut self) -> Option<Self::Item> {
				Some(self.0)
			}
		}

		/// Tier 3 — "iterate by reference" for concrete std container and
		/// reference types only (never a blanket over a user trait, so it can
		/// never coherence-collide with [`RepToTokensExt`]). `&self` receiver;
		/// reports `is_iterable = true`.
		pub trait RepAsIteratorExt<'q> {
			type Iter: Iterator;
			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool);
		}

		impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T {
			type Iter = T::Iter;
			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
				<T as RepAsIteratorExt>::__template_quote_into_iter(*self)
			}
		}

		impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &mut T {
			type Iter = T::Iter;
			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
				<T as RepAsIteratorExt>::__template_quote_into_iter(*self)
			}
		}

		impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] {
			type Iter = slice::Iter<'q, T>;
			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
				(self.iter(), true)
			}
		}

		impl<'q, T: 'q, const N: usize> RepAsIteratorExt<'q> for [T; N] {
			type Iter = slice::Iter<'q, T>;
			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
				(self.iter(), true)
			}
		}

		impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec<T> {
			type Iter = slice::Iter<'q, T>;
			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
				(self.iter(), true)
			}
		}

		impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet<T> {
			type Iter = btree_set::Iter<'q, T>;
			fn __template_quote_into_iter(&'q self) -> (Self::Iter, bool) {
				(self.iter(), true)
			}
		}
	}
}