summaryrefslogtreecommitdiff
path: root/src/diag.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-09-20 19:49:47 +0200
committerLaurenz <laurmaedje@gmail.com>2022-09-20 19:49:47 +0200
commit3760748fddd3b793c79c370398a9d4a3fc5afc04 (patch)
treeb1a615e510aa231cfe9757a9c0a35a375e32e3ba /src/diag.rs
parent757a701c1aa2a6fb80033c7e75666661818da6f9 (diff)
Refactor error handling
Diffstat (limited to 'src/diag.rs')
-rw-r--r--src/diag.rs122
1 files changed, 83 insertions, 39 deletions
diff --git a/src/diag.rs b/src/diag.rs
index e96dfb00..ebd192c2 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -2,24 +2,28 @@
use std::fmt::{self, Display, Formatter};
use std::io;
-use std::path::Path;
+use std::path::{Path, PathBuf};
use crate::syntax::{Span, Spanned};
use crate::World;
-/// Early-return with a [`TypError`].
+/// Early-return with a [`SourceError`].
#[macro_export]
macro_rules! bail {
+ ($error:expr) => {
+ return Err(Box::new(vec![$error]))
+ };
+
($($tts:tt)*) => {
- return Err($crate::error!($($tts)*).into())
+ $crate::bail!($crate::error!($($tts)*))
};
}
-/// Construct a [`TypError`].
+/// Construct a [`SourceError`].
#[macro_export]
macro_rules! error {
($span:expr, $message:expr $(,)?) => {
- Box::new(vec![$crate::diag::Error::new($span, $message)])
+ $crate::diag::SourceError::new($span, $message)
};
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
@@ -27,18 +31,12 @@ macro_rules! error {
};
}
-/// The result type for typesetting and all its subpasses.
-pub type TypResult<T> = Result<T, TypError>;
-
-/// The error type for typesetting and all its subpasses.
-pub type TypError = Box<Vec<Error>>;
-
-/// A result type with a string error message.
-pub type StrResult<T> = Result<T, String>;
+/// A result that can carry multiple source errors.
+pub type SourceResult<T> = Result<T, Box<Vec<SourceError>>>;
/// An error in a source file.
#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct Error {
+pub struct SourceError {
/// The erroneous node in the source code.
pub span: Span,
/// A diagnostic message describing the problem.
@@ -47,7 +45,7 @@ pub struct Error {
pub trace: Vec<Spanned<Tracepoint>>,
}
-impl Error {
+impl SourceError {
/// Create a new, bare error.
pub fn new(span: Span, message: impl Into<String>) -> Self {
Self {
@@ -58,7 +56,7 @@ impl Error {
}
}
-/// A part of an error's [trace](Error::trace).
+/// A part of an error's [trace](SourceError::trace).
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Tracepoint {
/// A function call.
@@ -83,22 +81,7 @@ impl Display for Tracepoint {
}
}
-/// Convert a [`StrResult`] to a [`TypResult`] by adding span information.
-pub trait At<T> {
- /// Add the span information.
- fn at(self, span: Span) -> TypResult<T>;
-}
-
-impl<T, S> At<T> for Result<T, S>
-where
- S: Into<String>,
-{
- fn at(self, span: Span) -> TypResult<T> {
- self.map_err(|message| error!(span, message))
- }
-}
-
-/// Enrich a [`TypResult`] with a tracepoint.
+/// Enrich a [`SourceResult`] with a tracepoint.
pub trait Trace<T> {
/// Add the tracepoint to all errors that lie outside the `span`.
fn trace<F>(self, world: &dyn World, make_point: F, span: Span) -> Self
@@ -106,7 +89,7 @@ pub trait Trace<T> {
F: Fn() -> Tracepoint;
}
-impl<T> Trace<T> for TypResult<T> {
+impl<T> Trace<T> for SourceResult<T> {
fn trace<F>(self, world: &dyn World, make_point: F, span: Span) -> Self
where
F: Fn() -> Tracepoint,
@@ -127,6 +110,9 @@ impl<T> Trace<T> for TypResult<T> {
}
}
+/// A result type with a string error message.
+pub type StrResult<T> = Result<T, String>;
+
/// Transform `expected X, found Y` into `expected X or A, found Y`.
pub fn with_alternative(msg: String, alt: &str) -> String {
let mut parts = msg.split(", found ");
@@ -137,12 +123,70 @@ pub fn with_alternative(msg: String, alt: &str) -> String {
}
}
-/// Format a file loading failure.
-pub fn failed_to_load(target: &str, path: &Path, error: io::Error) -> String {
- match error.kind() {
- io::ErrorKind::NotFound => {
- format!("file not found (searched at {})", path.display())
+/// Convert a [`StrResult`] to a [`SourceResult`] by adding span information.
+pub trait At<T> {
+ /// Add the span information.
+ fn at(self, span: Span) -> SourceResult<T>;
+}
+
+impl<T, S> At<T> for Result<T, S>
+where
+ S: Into<String>,
+{
+ fn at(self, span: Span) -> SourceResult<T> {
+ self.map_err(|message| Box::new(vec![error!(span, message)]))
+ }
+}
+
+/// A result type with a file-related error.
+pub type FileResult<T> = Result<T, FileError>;
+
+/// An error that occured while trying to load of a file.
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub enum FileError {
+ /// A file was not found at this path.
+ NotFound(PathBuf),
+ /// A file could not be accessed.
+ AccessDenied,
+ /// The file was not valid UTF-8, but should have been.
+ InvalidUtf8,
+ /// Another error.
+ Other,
+}
+
+impl FileError {
+ /// Create a file error from an I/O error.
+ pub fn from_io(error: io::Error, path: &Path) -> Self {
+ match error.kind() {
+ io::ErrorKind::NotFound => Self::NotFound(path.into()),
+ io::ErrorKind::PermissionDenied => Self::AccessDenied,
+ io::ErrorKind::InvalidData
+ if error.to_string().contains("stream did not contain valid UTF-8") =>
+ {
+ Self::InvalidUtf8
+ }
+ _ => Self::Other,
}
- _ => format!("failed to load {target} ({error})"),
+ }
+}
+
+impl std::error::Error for FileError {}
+
+impl Display for FileError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Self::NotFound(path) => {
+ write!(f, "file not found (searched at {})", path.display())
+ }
+ Self::AccessDenied => f.pad("file access denied"),
+ Self::InvalidUtf8 => f.pad("file is not valid utf-8"),
+ Self::Other => f.pad("failed to load file"),
+ }
+ }
+}
+
+impl From<FileError> for String {
+ fn from(error: FileError) -> Self {
+ error.to_string()
}
}