use crate::{
multi::{abort_now, push_error},
ResultExt,
};
use proc_macro2::{Span, TokenStream};
use quote::{quote_spanned, ToTokens};
use std::{
fmt::{Display, Formatter},
ops::{Deref, DerefMut},
};
#[macro_export]
macro_rules! macro_error {
($span:expr, $fmt:expr, $($args:expr),+) => {{
let msg = format!($fmt, $($args),*);
let span = $span.into();
$crate::MacroError::new(span, msg)
}};
($span:expr, $msg:expr) => {{
$crate::MacroError::new($span.into(), $msg.to_string())
}};
}
#[macro_export]
macro_rules! abort {
($span:expr, $fmt:expr, $($args:expr),*) => {{
use $crate::macro_error;
macro_error!($span, $fmt, $($args),*).abort()
}};
($span:expr, $msg:expr) => {{
use $crate::macro_error;
macro_error!($span, $msg).abort()
}};
($err:expr) => { $crate::MacroError::from($err).abort() };
}
#[macro_export]
macro_rules! abort_call_site {
($fmt:expr, $($args:expr),*) => {{
use $crate::abort;
let span = $crate::proc_macro2::Span::call_site();
abort!(span, $fmt, $($args),*)
}};
($msg:expr) => {{
use $crate::abort;
let span = $crate::proc_macro2::Span::call_site();
abort!(span, $msg)
}};
}
#[derive(Debug)]
pub struct MacroError {
pub(crate) span: Span,
pub(crate) 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 set_span(&mut self, span: Span) -> Span {
std::mem::replace(&mut self.span, span)
}
pub fn span(&self) -> Span {
self.span
}
pub fn abort(self) -> ! {
push_error(self);
abort_now()
}
}
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 ToTokens for MacroError {
fn to_tokens(&self, ts: &mut TokenStream) {
let MacroError { ref msg, ref span } = *self;
let msg = syn::LitStr::new(msg, *span);
ts.extend(quote_spanned!(*span=> compile_error!(#msg); ));
}
}
impl Display for MacroError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
Display::fmt(&self.msg, f)
}
}
impl<T, E: Into<MacroError>> ResultExt for Result<T, E> {
type Ok = T;
fn unwrap_or_abort(self) -> T {
match self {
Ok(res) => res,
Err(e) => e.into().abort(),
}
}
fn expect_or_abort(self, message: &str) -> T {
match self {
Ok(res) => res,
Err(e) => {
let MacroError { msg, span } = e.into();
abort!(span, "{}: {}", message, msg);
}
}
}
}
impl Deref for MacroError {
type Target = str;
fn deref(&self) -> &str {
&self.msg
}
}
impl DerefMut for MacroError {
fn deref_mut(&mut self) -> &mut str {
&mut self.msg
}
}