diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-07-02 19:59:52 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-07-02 20:07:43 +0200 |
| commit | ebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch) | |
| tree | 2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/diag.rs | |
| parent | 3ab19185093d7709f824b95b979060ce125389d8 (diff) | |
Move everything into `crates/` directory
Diffstat (limited to 'src/diag.rs')
| -rw-r--r-- | src/diag.rs | 376 |
1 files changed, 0 insertions, 376 deletions
diff --git a/src/diag.rs b/src/diag.rs deleted file mode 100644 index b5995be4..00000000 --- a/src/diag.rs +++ /dev/null @@ -1,376 +0,0 @@ -//! Diagnostics. - -use std::fmt::{self, Display, Formatter}; -use std::io; -use std::path::{Path, PathBuf}; -use std::str::Utf8Error; -use std::string::FromUtf8Error; - -use comemo::Tracked; - -use crate::file::PackageSpec; -use crate::syntax::{Span, Spanned}; -use crate::World; - -/// Early-return with a [`StrResult`] or [`SourceResult`]. -/// -/// If called with just a string and format args, returns with a -/// `StrResult`. If called with a span, a string and format args, returns -/// a `SourceResult`. -/// -/// ``` -/// bail!("bailing with a {}", "string result"); -/// bail!(span, "bailing with a {}", "source result"); -/// ``` -#[macro_export] -#[doc(hidden)] -macro_rules! __bail { - ($fmt:literal $(, $arg:expr)* $(,)?) => { - return Err($crate::diag::eco_format!($fmt, $($arg),*)) - }; - - ($error:expr) => { - return Err(Box::new(vec![$error])) - }; - - ($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => { - return Err(Box::new(vec![$crate::diag::SourceError::new( - $span, - $crate::diag::eco_format!($fmt, $($arg),*), - )])) - }; -} - -#[doc(inline)] -pub use crate::__bail as bail; - -/// Construct an [`EcoString`] or [`SourceError`]. -#[macro_export] -#[doc(hidden)] -macro_rules! __error { - ($fmt:literal $(, $arg:expr)* $(,)?) => { - $crate::diag::eco_format!($fmt, $($arg),*) - }; - - ($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => { - $crate::diag::SourceError::new( - $span, - $crate::diag::eco_format!($fmt, $($arg),*), - ) - }; -} - -#[doc(inline)] -pub use crate::__error as error; -#[doc(hidden)] -pub use ecow::{eco_format, EcoString}; - -/// A result that can carry multiple source errors. -pub type SourceResult<T> = Result<T, Box<Vec<SourceError>>>; - -/// An error in a source file. -/// -/// The contained spans will only be detached if any of the input source files -/// were detached. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct SourceError { - /// The span of the erroneous node in the source code. - pub span: Span, - /// A diagnostic message describing the problem. - pub message: EcoString, - /// The trace of function calls leading to the error. - pub trace: Vec<Spanned<Tracepoint>>, - /// Additonal hints to the user, indicating how this error could be avoided - /// or worked around. - pub hints: Vec<EcoString>, -} - -impl SourceError { - /// Create a new, bare error. - pub fn new(span: Span, message: impl Into<EcoString>) -> Self { - Self { - span, - trace: vec![], - message: message.into(), - hints: vec![], - } - } - - /// Adds user-facing hints to the error. - pub fn with_hints(mut self, hints: impl IntoIterator<Item = EcoString>) -> Self { - self.hints.extend(hints); - self - } -} - -/// A part of an error's [trace](SourceError::trace). -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum Tracepoint { - /// A function call. - Call(Option<EcoString>), - /// A show rule application. - Show(EcoString), - /// A module import. - Import, -} - -impl Display for Tracepoint { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Tracepoint::Call(Some(name)) => { - write!(f, "error occurred in this call of function `{}`", name) - } - Tracepoint::Call(None) => { - write!(f, "error occurred in this function call") - } - Tracepoint::Show(name) => { - write!(f, "error occurred while applying show rule to this {name}") - } - Tracepoint::Import => { - write!(f, "error occurred while importing this module") - } - } - } -} - -/// 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: Tracked<dyn World + '_>, make_point: F, span: Span) -> Self - where - F: Fn() -> Tracepoint; -} - -impl<T> Trace<T> for SourceResult<T> { - fn trace<F>(self, world: Tracked<dyn World + '_>, make_point: F, span: Span) -> Self - where - F: Fn() -> Tracepoint, - { - self.map_err(|mut errors| { - if span.is_detached() { - return errors; - } - - let trace_range = span.range(&*world); - for error in errors.iter_mut().filter(|e| !e.span.is_detached()) { - // Skip traces that surround the error. - if error.span.id() == span.id() { - let error_range = error.span.range(&*world); - if trace_range.start <= error_range.start - && trace_range.end >= error_range.end - { - continue; - } - } - - error.trace.push(Spanned::new(make_point(), span)); - } - errors - }) - } -} - -/// A result type with a string error message. -pub type StrResult<T> = Result<T, EcoString>; - -/// 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<EcoString>, -{ - fn at(self, span: Span) -> SourceResult<T> { - self.map_err(|message| Box::new(vec![SourceError::new(span, message)])) - } -} - -/// A result type with a string error message and hints. -pub type HintedStrResult<T> = Result<T, HintedString>; - -/// A string message with hints. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct HintedString { - /// A diagnostic message describing the problem. - pub message: EcoString, - /// Additonal hints to the user, indicating how this error could be avoided - /// or worked around. - pub hints: Vec<EcoString>, -} - -impl<T> At<T> for Result<T, HintedString> { - fn at(self, span: Span) -> SourceResult<T> { - self.map_err(|diags| { - Box::new(vec![SourceError::new(span, diags.message).with_hints(diags.hints)]) - }) - } -} - -/// Enrich a [`StrResult`] or [`HintedStrResult`] with a hint. -pub trait Hint<T> { - /// Add the hint. - fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T>; -} - -impl<T> Hint<T> for StrResult<T> { - fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T> { - self.map_err(|message| HintedString { message, hints: vec![hint.into()] }) - } -} - -impl<T> Hint<T> for HintedStrResult<T> { - fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T> { - self.map_err(|mut error| { - error.hints.push(hint.into()); - error - }) - } -} - -/// A result type with a file-related error. -pub type FileResult<T> = Result<T, FileError>; - -/// An error that occurred 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, - /// A directory was found, but a file was expected. - IsDirectory, - /// The file is not a Typst source file, but should have been. - NotSource, - /// The file was not valid UTF-8, but should have been. - InvalidUtf8, - /// The package the file is part of could not be loaded. - Package(PackageError), - /// 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, - } - } -} - -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("failed to load file (access denied)"), - Self::IsDirectory => f.pad("failed to load file (is a directory)"), - Self::NotSource => f.pad("not a typst source file"), - Self::InvalidUtf8 => f.pad("file is not valid utf-8"), - Self::Package(error) => error.fmt(f), - Self::Other => f.pad("failed to load file"), - } - } -} - -impl From<Utf8Error> for FileError { - fn from(_: Utf8Error) -> Self { - Self::InvalidUtf8 - } -} - -impl From<FromUtf8Error> for FileError { - fn from(_: FromUtf8Error) -> Self { - Self::InvalidUtf8 - } -} - -impl From<PackageError> for FileError { - fn from(error: PackageError) -> Self { - Self::Package(error) - } -} - -impl From<FileError> for EcoString { - fn from(error: FileError) -> Self { - eco_format!("{error}") - } -} - -/// A result type with a package-related error. -pub type PackageResult<T> = Result<T, PackageError>; - -/// An error that occured while trying to load a package. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum PackageError { - /// The specified package does not exist. - NotFound(PackageSpec), - /// Failed to retrieve the package through the network. - NetworkFailed, - /// The package archive was malformed. - MalformedArchive, - /// Another error. - Other, -} - -impl std::error::Error for PackageError {} - -impl Display for PackageError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::NotFound(spec) => { - write!(f, "package not found (searched for {spec})",) - } - Self::NetworkFailed => f.pad("failed to load package (network failed)"), - Self::MalformedArchive => f.pad("failed to load package (archive malformed)"), - Self::Other => f.pad("failed to load package"), - } - } -} - -impl From<PackageError> for EcoString { - fn from(error: PackageError) -> Self { - eco_format!("{error}") - } -} -/// Format a user-facing error message for an XML-like file format. -pub fn format_xml_like_error(format: &str, error: roxmltree::Error) -> EcoString { - match error { - roxmltree::Error::UnexpectedCloseTag { expected, actual, pos } => { - eco_format!( - "failed to parse {format}: found closing tag '{actual}' \ - instead of '{expected}' in line {}", - pos.row - ) - } - roxmltree::Error::UnknownEntityReference(entity, pos) => { - eco_format!( - "failed to parse {format}: unknown entity '{entity}' in line {}", - pos.row - ) - } - roxmltree::Error::DuplicatedAttribute(attr, pos) => { - eco_format!( - "failed to parse {format}: duplicate attribute '{attr}' in line {}", - pos.row - ) - } - roxmltree::Error::NoRootNode => { - eco_format!("failed to parse {format}: missing root node") - } - _ => eco_format!("failed to parse {format}"), - } -} |
