From 1a1ee75e0ee3edbdbdb0c22f494486c996ced6de Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 1 Jul 2024 06:25:54 +0200 Subject: [PATCH] adds a simple error type to allow producing nicer errors --- .../crates/scip-syntax/src/scip_strict.rs | 1 + .../src/scip_strict/context_error.rs | 49 ++++++++++++++++++ .../scip-syntax/src/scip_strict/parse.rs | 50 ++++++++++--------- 3 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/context_error.rs diff --git a/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict.rs b/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict.rs index 4e349028a751..63f27da4a672 100644 --- a/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict.rs +++ b/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +mod context_error; mod format; mod parse; diff --git a/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/context_error.rs b/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/context_error.rs new file mode 100644 index 000000000000..f7f45f3e5ddc --- /dev/null +++ b/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/context_error.rs @@ -0,0 +1,49 @@ +use std::fmt; + +use nom::error::{ContextError, ErrorKind, FromExternalError, ParseError}; + +/// default error type, only contains the error's location and code +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CtxError { + /// position of the error in the input data + pub input: I, + /// contextual error message + pub context: &'static str, +} + +impl ParseError for CtxError { + fn from_error_kind(input: I, _kind: ErrorKind) -> Self { + CtxError { + input, + context: "no context set yet", + } + } + + fn append(_: I, _: ErrorKind, other: Self) -> Self { + other + } +} + +impl ContextError for CtxError { + fn add_context(_input: I, context: &'static str, mut other: Self) -> Self { + other.context = context; + other + } +} + +impl FromExternalError for CtxError { + /// Create a new error from an input position and an external error + fn from_external_error(input: I, _kind: ErrorKind, _e: E) -> Self { + CtxError { + input, + context: "no context set yet", + } + } +} + +/// The Display implementation allows the std::error::Error implementation +impl fmt::Display for CtxError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "error '{}' at: {}", self.context, self.input) + } +} diff --git a/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/parse.rs b/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/parse.rs index 51fe9eee822b..c7f16403eabc 100644 --- a/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/parse.rs +++ b/docker-images/syntax-highlighter/crates/scip-syntax/src/scip_strict/parse.rs @@ -1,29 +1,26 @@ -use std::borrow::Cow; +use std::{borrow::Cow, str}; use nom::{ branch::alt, bytes::complete::{tag, take_while1}, character::complete::char, - combinator::{cut, eof, fail, opt}, - error::{context, convert_error, VerboseError}, + combinator::{cut, eof, fail, opt, verify}, + error::context, multi::many1, sequence::{delimited, preceded, tuple}, Finish, IResult, Parser, }; -use super::{Descriptor, NonLocalSymbol, Package, Scheme, Symbol}; +use super::{context_error::CtxError, Descriptor, NonLocalSymbol, Package, Scheme, Symbol}; pub(super) fn parse_symbol(input: &str) -> Result, String> { match parse_symbol_inner(input).finish() { Ok((_, symbol)) => Ok(symbol), - Err(err) => Err(format!( - "Invalid symbol: '{input}'\n{}", - convert_error(input, err) - )), + Err(err) => Err(format!("Invalid symbol: '{input}'\n{err}",)), } } -type PResult<'a, A> = IResult<&'a str, A, VerboseError<&'a str>>; +type PResult<'a, A> = IResult<&'a str, A, CtxError<&'a str>>; fn parse_symbol_inner(input: &str) -> PResult<'_, Symbol<'_>> { let (input, symbol) = alt((parse_local_symbol, parse_nonlocal_symbol))(input)?; @@ -38,26 +35,33 @@ fn parse_local_symbol(input: &str) -> PResult<'_, Symbol<'_>> { } fn parse_nonlocal_symbol(input: &str) -> PResult<'_, Symbol<'_>> { - tuple(( - parse_space_terminated, - parse_package, - many1(parse_descriptor), - )) - .map(|(scheme, package, descriptors)| { - Symbol::NonLocal(NonLocalSymbol { - scheme: Scheme(scheme), - package, - descriptors, + tuple((parse_scheme, parse_package, many1(parse_descriptor))) + .map(|(scheme, package, descriptors)| { + Symbol::NonLocal(NonLocalSymbol { + scheme, + package, + descriptors, + }) }) - }) + .parse(input) +} + +fn parse_scheme(input: &str) -> PResult<'_, Scheme> { + context( + "Invalid scheme", + verify(parse_space_terminated, |s: &Cow<'_, str>| { + !s.starts_with("local") + }), + ) + .map(Scheme) .parse(input) } fn parse_package(input: &str) -> PResult<'_, Package> { tuple(( - parse_space_terminated, - parse_space_terminated, - parse_space_terminated, + context("Invalid package manager", parse_space_terminated), + context("Invalid package name", parse_space_terminated), + context("Invalid package version", parse_space_terminated), )) .map(|(manager, package_name, version)| Package { manager,