pub extern crate proc_macro;
pub extern crate proc_macro2;
extern crate quote;
extern crate syn;
use proc_macro2::{Span, TokenStream};
use quote::quote_spanned;
#[macro_export]
macro_rules! span_error {
($span:expr, $fmt:literal, $($args:expr),*) => {{
let msg = format!($fmt, $($args),*);
let err = $crate::MacroError::new($span.into(), msg);
$crate::trigger_error(err)
}};
($span:expr, $msg:expr) => {{
let err = $crate::MacroError::new($span.into(), $msg.to_string());
$crate::trigger_error(err)
}};
($err:expr) => { $crate::trigger_error($err) };
}
#[macro_export]
macro_rules! call_site_error {
($fmt:literal, $($args:expr),*) => {{
use $crate::span_error;
let span = $crate::proc_macro2::Span::call_site();
span_error!(span, $fmt, $($args),*)
}};
($fmt:expr) => {{
use $crate::span_error;
let span = $crate::proc_macro2::Span::call_site();
span_error!(span, $fmt)
}};
}
#[macro_export]
macro_rules! filter_macro_errors {
($($code:tt)*) => {
let f = move || -> $crate::proc_macro::TokenStream { $($code)* };
$crate::filter_macro_error_panics(f)
};
}
#[derive(Debug)]
pub struct MacroError {
span: Span,
msg: String,
}
impl MacroError {
pub fn new(span: Span, msg: String) -> Self {
MacroError { span, msg }
}
pub fn call_site(msg: String) -> Self {
MacroError::new(Span::call_site(), msg)
}
pub fn into_compile_error(self) -> TokenStream {
let MacroError { span, msg } = self;
quote_spanned! { span=> compile_error!(#msg); }
}
pub fn set_span(&mut self, span: Span) {
self.span = span;
}
}
pub trait ResultExt {
type Ok;
fn unwrap_or_exit(self) -> Self::Ok;
fn expect_or_exit(self, msg: &str) -> Self::Ok;
}
pub trait OptionExt {
type Some;
fn expect_or_exit(self, msg: &str) -> Self::Some;
}
impl<T, E: Into<MacroError>> ResultExt for Result<T, E> {
type Ok = T;
fn unwrap_or_exit(self) -> T {
match self {
Ok(res) => res,
Err(e) => trigger_error(e),
}
}
fn expect_or_exit(self, message: &str) -> T {
match self {
Ok(res) => res,
Err(e) => {
let MacroError { msg, span } = e.into();
let msg = format!("{}: {}", message, msg);
trigger_error(MacroError::new(span, msg))
}
}
}
}
impl<T> OptionExt for Option<T> {
type Some = T;
fn expect_or_exit(self, message: &str) -> T {
match self {
Some(res) => res,
None => call_site_error!(message),
}
}
}
impl From<syn::Error> for MacroError {
fn from(e: syn::Error) -> Self {
MacroError::new(e.span(), e.to_string())
}
}
impl From<String> for MacroError {
fn from(msg: String) -> Self {
MacroError::call_site(msg)
}
}
impl From<&str> for MacroError {
fn from(msg: &str) -> Self {
MacroError::call_site(msg.into())
}
}
impl ToString for MacroError {
fn to_string(&self) -> String {
self.msg.clone()
}
}
pub fn trigger_error<T: Into<MacroError>>(err: T) -> ! {
panic!(Payload(err.into()))
}
pub fn filter_macro_error_panics<F>(f: F) -> proc_macro::TokenStream
where
F: FnOnce() -> proc_macro::TokenStream,
{
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
match catch_unwind(AssertUnwindSafe(f)) {
Ok(stream) => stream,
Err(boxed) => match boxed.downcast::<Payload>() {
Ok(err) => err.0.into_compile_error().into(),
Err(other) => resume_unwind(other),
},
}
}
struct Payload(MacroError);
unsafe impl Send for Payload {}
unsafe impl Sync for Payload {}