diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-09-20 19:49:47 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-09-20 19:49:47 +0200 |
| commit | 3760748fddd3b793c79c370398a9d4a3fc5afc04 (patch) | |
| tree | b1a615e510aa231cfe9757a9c0a35a375e32e3ba /src/diag.rs | |
| parent | 757a701c1aa2a6fb80033c7e75666661818da6f9 (diff) | |
Refactor error handling
Diffstat (limited to 'src/diag.rs')
| -rw-r--r-- | src/diag.rs | 122 |
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() } } |
