summaryrefslogtreecommitdiff
path: root/src/diag.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/diag.rs
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'src/diag.rs')
-rw-r--r--src/diag.rs376
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}"),
- }
-}