#![forbid(unsafe_code)]
#![no_std]
use proc_macro::{Group, Literal, Punct, Spacing, TokenStream, TokenTree};
use quote::quote_spanned;
use syn::{parse, LitByteStr, LitStr};
#[proc_macro]
pub fn expand(input: TokenStream) -> TokenStream {
let mut input = input.into_iter();
let mut output = TokenStream::new();
loop {
match input.next() {
Some(TokenTree::Group(t)) => {
output.extend(Some(TokenTree::Group(Group::new(
t.delimiter(),
expand(t.stream()),
))));
}
Some(TokenTree::Punct(t)) if t == '@' => {
let tt = if let Some(tt) = input.next() {
tt
} else {
output.extend(Some(TokenTree::Punct(t)));
break;
};
if let Ok(t) = parse::<LitByteStr>(tt.clone().into()) {
let mut xs = t
.value()
.into_iter()
.map(Literal::u8_suffixed)
.map(TokenTree::Literal);
if let Some(x) = xs.next() {
output.extend(Some(x));
} else {
output.extend::<TokenStream>(
quote_spanned! { tt.span().into() =>
compile_error!("can't expand an empty byte string")
}
.into(),
);
break;
}
for x in xs {
output.extend([TokenTree::Punct(Punct::new(',', Spacing::Alone)), x]);
}
} else if let Ok(t) = parse::<LitStr>(tt.clone().into()) {
let xs = t.value();
let mut xs = xs.chars().map(Literal::character).map(TokenTree::Literal);
if let Some(x) = xs.next() {
output.extend(Some(x));
} else {
output.extend::<TokenStream>(
quote_spanned! { tt.span().into() =>
compile_error!("can't expand an empty string")
}
.into(),
);
break;
}
for x in xs {
output.extend([TokenTree::Punct(Punct::new(',', Spacing::Alone)), x]);
}
} else {
output.extend([TokenTree::Punct(t), tt]);
continue;
}
}
Some(t) => {
output.extend(Some(t));
}
None => break,
}
}
output
}