Skip to content

Commit

Permalink
Add Errors stack to nix-parser2
Browse files Browse the repository at this point in the history
This allows for generic, crate-agnostic error handling which can be used
in either `nix-parser` or a future `nix-eval` crate later on.
  • Loading branch information
ebkalderon committed Mar 25, 2020
1 parent d5019aa commit 76d99ed
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions nix-parser2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ serialization = ["codespan/serialization", "serde"]

[dependencies]
codespan = "0.9.1"
codespan-reporting = "0.9"
lsp-types = "0.73"
nom = { version = "5.1", default-features = false }
nom_locate = "2.0"
smallvec = "1.2"
Expand Down
79 changes: 79 additions & 0 deletions nix-parser2/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! Common types and traits used by all kinds of errors.

use std::fmt::{self, Display, Formatter};
use std::slice::Iter;

use codespan::FileId;
use codespan_reporting::diagnostic::Diagnostic;
use lsp_types::Diagnostic as LspDiagnostic;
use smallvec::SmallVec;

/// A trait for converting an error type into a reportable diagnostic.
Expand All @@ -14,6 +17,82 @@ pub trait ToDiagnostic<D> {
fn to_diagnostic(&self, file_id: FileId) -> D;
}

/// A generic growable stack for accumulating errors.
#[derive(Clone, Debug, PartialEq)]
pub struct Errors<E>(SmallVec<[E; 4]>);

impl<E> Errors<E> {
/// Constructs a new, empty `Errors` stack.
///
/// The stack will not allocate until new errors are pushed onto it.
#[inline]
pub fn new() -> Self {
Errors(SmallVec::new())
}

/// Returns the number of errors in the stack.
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}

/// Returns `true` if the error stack is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

/// Appends a new error to the stack.
pub fn push(&mut self, error: E) {
self.0.push(error);
}

/// Removes the last error from the stack and returns it, or [`None`] if it is empty.
///
/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
pub fn pop(&mut self) -> Option<E> {
self.0.pop()
}

/// Returns the last error in the stack, or [`None`] if it is empty.
///
/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
pub fn last(&self) -> Option<&E> {
self.0.last()
}

/// Returns an iterator of errors.
pub fn iter(&self) -> Iter<E> {
self.0.iter()
}
}

impl<E> Errors<E>
where
E: ToDiagnostic<Diagnostic<FileId>>,
{
/// Returns an iterator which yields each error converted to a [`Diagnostic`].
///
/// [`Diagnostic`]: https://docs.rs/codespan-reporting/0.9.1/codespan_reporting/diagnostic/struct.Diagnostic.html
#[inline]
pub fn to_diagnostics(&self, file_id: FileId) -> impl Iterator<Item = Diagnostic<FileId>> + '_ {
self.iter().map(move |e| e.to_diagnostic(file_id))
}
}

impl<E> Errors<E>
where
E: ToDiagnostic<LspDiagnostic>,
{
/// Returns an iterator which yields each error converted to an LSP [`Diagnostic`].
///
/// [`Diagnostic`]: https://docs.rs/lsp-types/0.73.0/lsp_types/struct.Diagnostic.html
#[inline]
pub fn to_lsp_diagnostics(&self, file_id: FileId) -> impl Iterator<Item = LspDiagnostic> + '_ {
self.iter().map(move |e| e.to_diagnostic(file_id))
}
}

/// Helper struct for producing pretty "expected foo, found bar" error messages.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExpectedFound<T, U = T> {
Expand Down

0 comments on commit 76d99ed

Please sign in to comment.