pmutil/spanned_quote/
buffer.rsuse crate::respan::{self, Respan};
use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
use quote::{ToTokens, TokenStreamExt};
use std::collections::HashSet;
use std::env;
use std::fmt::{self, Display, Formatter, Write};
use syn::parse::Parse;
pub struct Quote {
tts: TokenStream,
span: Option<Box<(dyn Respan + 'static)>>,
sources: HashSet<Location>,
}
const INVALID_SPAN_STATE: &'static str = "Span is in invalid state.
Closure provided to push_group should not panic.";
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Location {
pub file_name: &'static str,
pub line: u32,
pub col: u32,
}
impl Display for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}:{}:{}", self.file_name, self.line, self.col)?;
Ok(())
}
}
impl Quote {
pub fn new<S: Respan + 'static>(span: S) -> Self {
Quote {
span: Some(Box::new(span)),
tts: TokenStream::new(),
sources: Default::default(),
}
}
pub fn new_call_site() -> Self {
Self::new(Span::call_site())
}
pub fn from_tokens(tokens: &dyn ToTokens) -> Self {
Self::new(respan::FirstLast::from_tokens(tokens))
}
pub fn from_tokens_or<T: ToTokens>(tokens: &Option<T>, default_span: Span) -> Self {
match *tokens {
Some(ref tokens) => Self::from_tokens(tokens),
None => Self::new(default_span),
}
}
}
impl Quote {
pub fn parse<Node>(self) -> Node
where
Node: Parse,
{
let Quote { tts, sources, .. } = self;
let debug_tts = if env::var("DBG_DUMP").is_ok() {
Some(tts.clone())
} else {
None
};
syn::parse2(tts).unwrap_or_else(|err| {
let debug_tts: &dyn Display = match debug_tts {
Some(ref tts) => tts,
None => {
&"To get code failed to parse,
please set environment variable `DBG_DUMP` and run in again"
}
};
let notes = {
let mut b = String::from("Note: quasi quotting was invoked from:\n");
for src in &sources {
writeln!(b, " {}", src).unwrap();
}
b
};
panic!(
"Quote::parse() failed.
{notes}
Error from syn: {err}
>>>>>
{debug_tts}
<<<<<",
notes = notes,
err = err,
debug_tts = debug_tts
)
})
}
}
impl Quote {
#[doc(hidden)]
pub fn report_loc(&mut self, loc: Location) {
self.sources.insert(loc);
}
pub fn quote_with<F>(mut self, quote: F) -> Self
where
F: FnOnce(&mut Self),
{
(quote)(&mut self);
self
}
pub fn push_parsed(&mut self, token: &str) {
let Quote {
ref mut span,
ref mut tts,
..
} = *self;
token
.parse::<TokenStream>()
.expect("Failed to parse token to quote")
.into_iter()
.map(|tt| span.as_ref().expect(INVALID_SPAN_STATE).respan(tt))
.for_each(|tt| tts.append(tt));
}
pub fn push_tt(&mut self, tt: TokenTree) {
self.tts.append(tt)
}
pub fn push_sym(&mut self, term: &str) {
let span = self.next_span();
self.push_tt(TokenTree::Ident(Ident::new(term, span)))
}
fn next_span(&self) -> Span {
self.span.as_ref().expect(INVALID_SPAN_STATE).next_span()
}
pub fn push_group<F>(&mut self, delim: Delimiter, child: F)
where
F: FnOnce(&mut Quote),
{
let span = self.span.take().expect(INVALID_SPAN_STATE);
let mut sub = Quote::new(span);
child(&mut sub);
self.sources.extend(sub.sources);
debug_assert!(self.span.is_none());
self.span = Some(sub.span.expect(INVALID_SPAN_STATE));
self.push_tt(TokenTree::Group(Group::new(delim, sub.tts)))
}
pub fn push_tokens<T: ?Sized + ToTokens>(&mut self, node: &T) {
node.to_tokens(&mut self.tts);
}
}
impl IntoIterator for Quote {
type IntoIter = <TokenStream as IntoIterator>::IntoIter;
type Item = <TokenStream as IntoIterator>::Item;
fn into_iter(self) -> Self::IntoIter {
self.tts.into_iter()
}
}
impl From<Quote> for TokenStream {
fn from(quote: Quote) -> Self {
quote.tts
}
}
impl From<Quote> for proc_macro::TokenStream {
fn from(quote: Quote) -> Self {
TokenStream::from(quote).into()
}
}
impl ToTokens for Quote {
fn to_tokens(&self, dst: &mut TokenStream) {
self.tts.to_tokens(dst)
}
fn into_token_stream(self) -> TokenStream {
self.tts
}
}