use std::error::Error as StdError;
use std::fmt::{Debug, Display, Write};
use std::ops::{Deref, DerefMut, Index, Range};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub fn join(&self, other: Span) -> Span {
Span {
start: self.start,
end: other.end,
}
}
pub fn range(&self) -> Range<usize> {
Range {
start: self.start,
end: self.end,
}
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
}
impl From<Range<usize>> for Span {
fn from(value: Range<usize>) -> Self {
Self {
start: value.start,
end: value.end,
}
}
}
impl From<Span> for Range<usize> {
fn from(value: Span) -> Self {
Range {
start: value.start,
end: value.end,
}
}
}
impl std::fmt::Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
impl Index<Span> for [u8] {
type Output = [u8];
fn index(&self, index: Span) -> &Self::Output {
&self[index.range()]
}
}
impl Index<Span> for str {
type Output = str;
fn index(&self, index: Span) -> &Self::Output {
&self[index.range()]
}
}
#[derive(Clone, Copy, Default)]
pub struct Spanned<T> {
pub span: Span,
value: T,
}
impl<T> Spanned<T> {
pub fn new(span: impl Into<Span>, value: T) -> Spanned<T> {
Spanned {
span: span.into(),
value,
}
}
pub fn into_inner(self) -> T {
self.value
}
}
impl<T> Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> DerefMut for Spanned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T: std::hash::Hash> std::hash::Hash for Spanned<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<T: std::cmp::PartialEq> std::cmp::PartialEq for Spanned<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: std::cmp::Eq> std::cmp::Eq for Spanned<T> {}
impl<T: std::cmp::PartialOrd> std::cmp::PartialOrd for Spanned<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<T: std::cmp::Ord> std::cmp::Ord for Spanned<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(&other.value)
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for Spanned<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<T: std::fmt::Display> std::fmt::Display for Spanned<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
#[derive(Clone, Debug)]
pub struct SpannedError {
pub span: Span,
pub message: String,
}
pub trait MaybeSpan {
fn into_span(self) -> Span;
}
impl MaybeSpan for Span {
fn into_span(self) -> Span {
self
}
}
impl MaybeSpan for Range<usize> {
fn into_span(self) -> Span {
self.into()
}
}
impl MaybeSpan for Option<Range<usize>> {
fn into_span(self) -> Span {
match self {
Some(v) => v.into(),
None => (0..0).into(),
}
}
}
impl SpannedError {
pub fn new(message: impl ToString, span: impl MaybeSpan) -> Self {
Self {
span: span.into_span(),
message: message.to_string(),
}
}
pub fn report(&self, src: &str, use_color: bool) -> String {
if self.span.is_empty() {
return self.message.clone();
}
if self.span.start > src.len() || self.span.end > src.len() {
return self.message.clone();
}
let start = src[..self.span.start].rfind('\n').unwrap_or(0);
let end = src[self.span.end..]
.find('\n')
.map(|v| v + self.span.end)
.unwrap_or(src.len());
let (r, c) = if use_color {
("\x1b[0m", "\x1b[4;31m")
} else {
("", "")
};
let pre = &src[start..self.span.start].trim_start();
let content = &src[self.span.start..self.span.end];
let post = &src[self.span.end..end].trim_end();
let mut out = String::new();
let f = &mut out;
writeln!(f, "{}", self.message).unwrap();
let mut lines = content.lines().peekable();
let line = lines.next().unwrap().or("_");
if lines.peek().is_some() {
writeln!(f, "| {pre}{c}{line}{r}").unwrap();
while let Some(line) = lines.next() {
let line = line.or("_");
if lines.peek().is_some() {
writeln!(f, "| {c}{line}{r}").unwrap();
} else {
write!(f, "| {c}{line}{r}{post}").unwrap();
}
}
} else {
writeln!(f, "| {pre}{c}{line}{r}{post}").unwrap();
}
out
}
}
trait EmptyOr {
fn or<'a>(&'a self, v: &'a Self) -> &'a Self;
}
impl EmptyOr for str {
fn or<'a>(&'a self, v: &'a Self) -> &'a Self {
if self.is_empty() {
v
} else {
self
}
}
}
impl Display for SpannedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl StdError for SpannedError {}
#[cfg(all(test, not(feature = "__miri")))]
mod tests;