use proc_macro2::{TokenStream, TokenTree};
use quote::{ToTokens, quote};
use syn::parse::Parse;
use crate::parse::helpers::is_helper;
pub(crate) fn error_into_token_stream(err: syn::Error, item: TokenStream) -> TokenStream {
let compile_error = err.to_compile_error();
syn::parse2::<Fallback>(item)
.map(|fallback| quote!(#compile_error #fallback))
.unwrap_or_else(|_| compile_error)
}
struct Fallback {
output: TokenStream,
}
impl Parse for Fallback {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let mut output = TokenStream::new();
loop {
let found_attr = input.step(|cursor| {
let mut cursor = *cursor;
while let Some((tt, next)) = cursor.token_tree() {
match &tt {
TokenTree::Group(group) => {
let fallback: Self = syn::parse2(group.stream())?;
let new_group =
proc_macro2::Group::new(group.delimiter(), fallback.output);
output.extend([TokenTree::Group(new_group)]);
}
TokenTree::Punct(punct) if punct.as_char() == '#' => {
return Ok((true, cursor));
}
TokenTree::Punct(_) | TokenTree::Ident(_) | TokenTree::Literal(_) => {
output.extend([tt]);
}
}
cursor = next;
}
Ok((false, cursor))
})?;
if !found_attr {
return Ok(Self { output });
}
input
.call(syn::Attribute::parse_outer)?
.into_iter()
.filter(|attr| !is_helper(attr))
.for_each(|attr| attr.to_tokens(&mut output));
}
}
}
impl ToTokens for Fallback {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.output.to_tokens(tokens);
}
}