use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::ToTokens;
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
parse_macro_input,
LitStr,
Result,
Error,
Token,
};
type Literals = Punctuated<LitStr, Token![,]>;
struct LitStrList {
lits: Literals,
span: Span,
}
impl Parse for LitStrList {
fn parse(input: ParseStream) -> Result<Self> {
let span = input.span();
let lits = Literals::parse_terminated(input)?;
if lits.len() < 1 {
return Err(Error::new(
span, "Expected at least one literal"
));
}
Ok(Self { lits, span })
}
}
#[proc_macro]
pub fn errorlit(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStrList);
let name = match get_name() {
Err(e) => return e.to_compile_error().into(),
Ok(v) => v,
};
let mut iter = input.lits.iter();
let first = iter.next().expect("At least one string is present");
let mut error = format!(
"\n\n\n\n[{name}] Fatal Error: {}\n",
first.value()
);
for msg in iter {
error.push_str(" -> ");
error.push_str(&msg.value());
error.push('\n');
}
error.push_str("\n\n\n\n");
let literal = LitStr::new(&error, input.span);
literal.to_token_stream().into()
}
fn get_name() -> syn::Result<String> {
let crate_name = match std::env::var("CARGO_PKG_NAME") {
Ok(variable) => variable,
Err(_) => return Err(Error::new(
Span::mixed_site(),
"Unable to identify crate's name :("
))
};
let mut characters = crate_name.chars();
let uppercased = match characters.next() {
None => String::new(),
Some(c) => c.to_uppercase().collect::<String>() + characters.as_str(),
};
Ok(uppercased)
}