#![doc = include_str!("../README.md")]
use {
proc_macro2::{Delimiter, Group, TokenStream, TokenTree},
quote::{quote, quote_spanned},
std::fmt::Write,
};
#[proc_macro]
pub fn literify(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
expand(ts.into()).into()
}
fn expand(ts: TokenStream) -> TokenStream {
let mut ts = ts.into_iter();
let mut out = TokenStream::default();
loop {
match ts.next() {
Some(TokenTree::Punct(p)) if p.as_char() == '~' => match ts.next() {
Some(TokenTree::Group(grp)) if grp.delimiter() == Delimiter::Parenthesis => {
let mut literal = String::new();
for tt in grp.stream() {
match tt {
TokenTree::Ident(id) => write!(literal, "{id}").unwrap(),
TokenTree::Literal(lit) => {
let span = lit.span();
if let litrs::Literal::String(lit) = litrs::Literal::from(lit) {
write!(literal, "{}", lit.value()).unwrap()
} else {
return quote_spanned!(span => compile_error!("Unsupported literal"));
}
}
TokenTree::Punct(punct) => write!(literal, "{punct}").unwrap(),
tt => {
return quote_spanned!(
tt.span() =>
compile_error!("Unsupported token tree (only string literals and identifiers are allowed)")
)
}
}
}
out.extend(quote!(#literal));
}
Some(TokenTree::Group(grp)) => out.extend([
TokenTree::Punct(p),
TokenTree::Group(Group::new(grp.delimiter(), expand(grp.stream()))),
]),
Some(tt) => out.extend([TokenTree::Punct(p), tt]),
None => break,
},
Some(TokenTree::Group(grp)) => out.extend([TokenTree::Group(Group::new(
grp.delimiter(),
expand(grp.stream()),
))]),
Some(tt) => out.extend([tt]),
None => break,
}
}
out
}