use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::fmt::{Debug, Display};
#[derive(Clone)]
pub struct Error {
messages: Vec<ErrorMessage>,
}
#[derive(Clone)]
struct ErrorMessage {
start_span: Span,
end_span: Span,
message: String,
}
impl Error {
pub fn new<T: Display>(message: T) -> Self {
Error {
messages: vec![ErrorMessage {
start_span: Span::call_site(),
end_span: Span::call_site(),
message: message.to_string(),
}],
}
}
pub fn new_at_span<T: Display>(span: Span, message: T) -> Self {
Error {
messages: vec![ErrorMessage {
start_span: span,
end_span: span,
message: message.to_string(),
}],
}
}
pub fn new_at_tokens<T: ToTokens, U: Display>(tokens: T, message: U) -> Self {
let mut iter = tokens.into_token_stream().into_iter();
let start = iter.next().map_or_else(Span::call_site, |t| t.span());
let end = iter.last().map_or(start, |t| t.span());
Error {
messages: vec![ErrorMessage {
start_span: start,
end_span: end,
message: message.to_string(),
}],
}
}
pub fn span(&self) -> Span {
let start = self.messages[0].start_span;
let end = self.messages[0].end_span;
start.join(end).unwrap_or(start)
}
pub fn to_compile_error(&self) -> TokenStream {
self.messages
.iter()
.map(ErrorMessage::to_compile_error)
.collect()
}
pub fn combine(&mut self, another: Error) {
self.messages.extend(another.messages);
}
}
impl ErrorMessage {
fn to_compile_error(&self) -> TokenStream {
TokenStream::from_iter(vec![
TokenTree::Ident(Ident::new("compile_error", self.start_span)),
TokenTree::Punct({
let mut punct = Punct::new('!', Spacing::Alone);
punct.set_span(self.start_span);
punct
}),
TokenTree::Group({
let mut group = Group::new(Delimiter::Brace, {
TokenStream::from_iter(vec![TokenTree::Literal({
let mut string = Literal::string(&self.message);
string.set_span(self.end_span);
string
})])
});
group.set_span(self.end_span);
group
}),
])
}
}
impl std::error::Error for Error {}
impl Debug for Error {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.messages.len() == 1 {
formatter
.debug_tuple("Error")
.field(&self.messages[0])
.finish()
} else {
formatter
.debug_tuple("Error")
.field(&self.messages)
.finish()
}
}
}
impl Debug for ErrorMessage {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
Debug::fmt(&self.message, formatter)
}
}
impl Display for Error {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str(&self.messages[0].message)
}
}