2020-06-16 20:00:11 -04:00
|
|
|
use std::{
|
|
|
|
error::Error,
|
|
|
|
fmt::{self, Display},
|
|
|
|
io,
|
|
|
|
string::FromUtf8Error,
|
|
|
|
};
|
2020-01-04 22:55:04 -05:00
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
use codemap::{Span, SpanLoc};
|
2020-03-01 14:53:52 -05:00
|
|
|
|
2020-06-26 05:12:28 -04:00
|
|
|
pub type SassResult<T> = Result<T, Box<SassError>>;
|
2020-02-16 10:08:45 -05:00
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
#[derive(Debug)]
|
2020-01-06 18:26:07 -05:00
|
|
|
pub struct SassError {
|
2020-04-12 19:37:12 -04:00
|
|
|
kind: SassErrorKind,
|
2020-01-06 18:26:07 -05:00
|
|
|
}
|
|
|
|
|
2020-07-03 15:17:04 -04:00
|
|
|
// todo: we should split the unclonable errors (io, potentially others) into
|
|
|
|
// a separate enum to allow these methods to be infallible
|
|
|
|
#[allow(clippy::unimplemented)]
|
2020-07-03 15:06:26 -04:00
|
|
|
impl Clone for SassError {
|
2020-07-03 15:17:04 -04:00
|
|
|
#[inline]
|
2020-07-03 15:06:26 -04:00
|
|
|
fn clone(&self) -> Self {
|
|
|
|
match &self.kind {
|
|
|
|
SassErrorKind::Raw(a, b) => SassError {
|
|
|
|
kind: SassErrorKind::Raw(a.clone(), *b),
|
|
|
|
},
|
|
|
|
SassErrorKind::ParseError { message, loc } => SassError {
|
|
|
|
kind: SassErrorKind::ParseError {
|
|
|
|
message: message.clone(),
|
|
|
|
loc: loc.clone(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 15:17:04 -04:00
|
|
|
#[allow(clippy::unimplemented)]
|
2020-07-03 15:06:26 -04:00
|
|
|
impl PartialEq for SassError {
|
2020-07-03 15:17:04 -04:00
|
|
|
#[inline]
|
2020-07-03 15:06:26 -04:00
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
match &self.kind {
|
|
|
|
SassErrorKind::Raw(a, b) => match &other.kind {
|
|
|
|
SassErrorKind::Raw(c, d) => a == c && b == d,
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
SassErrorKind::ParseError {
|
|
|
|
message: message1,
|
|
|
|
loc: loc1,
|
|
|
|
} => match &other.kind {
|
|
|
|
SassErrorKind::ParseError {
|
|
|
|
message: message2,
|
|
|
|
loc: loc2,
|
|
|
|
} => message1 == message2 && loc1 == loc2,
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for SassError {}
|
|
|
|
|
2020-01-06 18:26:07 -05:00
|
|
|
impl SassError {
|
2020-04-12 19:37:12 -04:00
|
|
|
pub(crate) fn raw(self) -> (String, Span) {
|
|
|
|
match self.kind {
|
|
|
|
SassErrorKind::Raw(string, span) => (string, span),
|
2020-04-24 22:57:39 -04:00
|
|
|
e => todo!("unable to get raw of {:?}", e),
|
2020-04-12 19:37:12 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-31 15:48:11 -04:00
|
|
|
pub(crate) const fn from_loc(message: String, loc: SpanLoc) -> Self {
|
2020-01-06 19:23:52 -05:00
|
|
|
SassError {
|
2020-04-12 19:37:12 -04:00
|
|
|
kind: SassErrorKind::ParseError { message, loc },
|
2020-01-06 19:23:52 -05:00
|
|
|
}
|
2020-01-06 18:26:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
enum SassErrorKind {
|
|
|
|
/// A raw error with no additional metadata
|
|
|
|
/// It contains only a `String` message and
|
|
|
|
/// a span
|
|
|
|
Raw(String, Span),
|
|
|
|
ParseError {
|
|
|
|
message: String,
|
|
|
|
loc: SpanLoc,
|
|
|
|
},
|
|
|
|
IoError(io::Error),
|
|
|
|
FromUtf8Error(String),
|
|
|
|
}
|
|
|
|
|
2020-01-06 18:26:07 -05:00
|
|
|
impl Display for SassError {
|
2020-04-14 22:40:19 -04:00
|
|
|
// TODO: trim whitespace from start of line shown in error
|
2020-04-12 19:37:12 -04:00
|
|
|
// TODO: color errors
|
|
|
|
// TODO: integrate with codemap-diagnostics
|
2020-04-21 18:22:26 -04:00
|
|
|
#[inline]
|
2020-01-06 18:26:07 -05:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2020-04-12 19:37:12 -04:00
|
|
|
let (message, loc) = match &self.kind {
|
|
|
|
SassErrorKind::ParseError { message, loc } => (message, loc),
|
2020-04-23 11:33:42 -04:00
|
|
|
SassErrorKind::FromUtf8Error(s) => return writeln!(f, "Error: {}", s),
|
|
|
|
SassErrorKind::IoError(s) => return writeln!(f, "Error: {}", s),
|
|
|
|
SassErrorKind::Raw(..) => todo!(),
|
2020-04-12 19:37:12 -04:00
|
|
|
};
|
|
|
|
let line = loc.begin.line + 1;
|
|
|
|
let col = loc.begin.column + 1;
|
|
|
|
writeln!(f, "Error: {}", message)?;
|
|
|
|
let padding = vec![' '; format!("{}", line).len() + 1]
|
|
|
|
.iter()
|
|
|
|
.collect::<String>();
|
|
|
|
writeln!(f, "{}|", padding)?;
|
|
|
|
writeln!(f, "{} | {}", line, loc.file.source_line(loc.begin.line))?;
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"{}| {}{}",
|
|
|
|
padding,
|
|
|
|
vec![' '; loc.begin.column].iter().collect::<String>(),
|
2020-05-24 12:19:08 -04:00
|
|
|
vec!['^'; loc.end.column.max(loc.begin.column) - loc.begin.column.min(loc.end.column)]
|
2020-04-12 19:37:12 -04:00
|
|
|
.iter()
|
|
|
|
.collect::<String>()
|
|
|
|
)?;
|
|
|
|
writeln!(f, "{}|", padding)?;
|
|
|
|
writeln!(f, "./{}:{}:{}", loc.file.name(), line, col)?;
|
|
|
|
Ok(())
|
2020-01-06 18:26:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:12:28 -04:00
|
|
|
impl From<io::Error> for Box<SassError> {
|
2020-04-21 18:22:26 -04:00
|
|
|
#[inline]
|
2020-06-26 05:12:28 -04:00
|
|
|
fn from(error: io::Error) -> Box<SassError> {
|
|
|
|
Box::new(SassError {
|
2020-04-12 19:37:12 -04:00
|
|
|
kind: SassErrorKind::IoError(error),
|
2020-06-26 05:12:28 -04:00
|
|
|
})
|
2020-01-06 18:26:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:12:28 -04:00
|
|
|
impl From<FromUtf8Error> for Box<SassError> {
|
2020-04-21 18:22:26 -04:00
|
|
|
#[inline]
|
2020-06-26 05:12:28 -04:00
|
|
|
fn from(error: FromUtf8Error) -> Box<SassError> {
|
|
|
|
Box::new(SassError {
|
2020-04-12 19:37:12 -04:00
|
|
|
kind: SassErrorKind::FromUtf8Error(format!(
|
|
|
|
"Invalid UTF-8 character \"\\x{:X?}\"",
|
|
|
|
error.as_bytes()[0]
|
|
|
|
)),
|
2020-06-26 05:12:28 -04:00
|
|
|
})
|
2020-01-20 12:13:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:12:28 -04:00
|
|
|
impl From<(&str, Span)> for Box<SassError> {
|
2020-02-16 10:54:25 -05:00
|
|
|
#[inline]
|
2020-06-26 05:12:28 -04:00
|
|
|
fn from(error: (&str, Span)) -> Box<SassError> {
|
|
|
|
Box::new(SassError {
|
2020-04-12 19:37:12 -04:00
|
|
|
kind: SassErrorKind::Raw(error.0.to_owned(), error.1),
|
2020-06-26 05:12:28 -04:00
|
|
|
})
|
2020-02-16 10:54:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:12:28 -04:00
|
|
|
impl From<(String, Span)> for Box<SassError> {
|
2020-02-16 10:54:25 -05:00
|
|
|
#[inline]
|
2020-06-26 05:12:28 -04:00
|
|
|
fn from(error: (String, Span)) -> Box<SassError> {
|
|
|
|
Box::new(SassError {
|
2020-04-12 19:37:12 -04:00
|
|
|
kind: SassErrorKind::Raw(error.0, error.1),
|
2020-06-26 05:12:28 -04:00
|
|
|
})
|
2020-02-16 10:54:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-06 18:26:07 -05:00
|
|
|
impl Error for SassError {
|
2020-04-21 18:22:26 -04:00
|
|
|
#[inline]
|
2020-01-06 18:26:07 -05:00
|
|
|
fn description(&self) -> &'static str {
|
2020-06-20 06:31:43 -04:00
|
|
|
"Sass parsing error"
|
2020-01-06 18:26:07 -05:00
|
|
|
}
|
|
|
|
}
|