From a62c5102c434151c3c70d8099afead50fddab213 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 8 Feb 2024 18:52:19 -0500 Subject: [PATCH] Convert all Error + ErrorKind into one error --- Cargo.lock | 3 + lib/oxigraph/src/storage/error.rs | 47 ++++----------- lib/oxrdf/Cargo.toml | 1 + lib/oxrdf/src/parser.rs | 86 ++++++++------------------- lib/oxrdfio/src/error.rs | 77 +++++------------------- lib/oxrdfxml/src/error.rs | 66 +++++---------------- lib/oxrdfxml/src/parser.rs | 26 +++------ lib/oxsdatatypes/Cargo.toml | 3 + lib/oxsdatatypes/src/date_time.rs | 54 ++++------------- lib/oxsdatatypes/src/decimal.rs | 66 ++++++--------------- lib/sparesults/src/csv.rs | 34 +++++------ lib/sparesults/src/error.rs | 97 +++++++++---------------------- lib/spargebra/Cargo.toml | 1 + lib/spargebra/src/parser.rs | 53 ++++------------- 14 files changed, 168 insertions(+), 446 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55b7dc02..85d35123 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1136,6 +1136,7 @@ dependencies = [ "oxiri", "oxsdatatypes", "rand", + "thiserror", ] [[package]] @@ -1176,6 +1177,7 @@ name = "oxsdatatypes" version = "0.2.0-alpha.1" dependencies = [ "js-sys", + "thiserror", ] [[package]] @@ -1761,6 +1763,7 @@ dependencies = [ "oxrdf", "peg", "rand", + "thiserror", ] [[package]] diff --git a/lib/oxigraph/src/storage/error.rs b/lib/oxigraph/src/storage/error.rs index 72f8b5e2..1b13bd9b 100644 --- a/lib/oxigraph/src/storage/error.rs +++ b/lib/oxigraph/src/storage/error.rs @@ -1,7 +1,7 @@ use crate::io::{ParseError, RdfFormat}; use oxiri::IriParseError; use std::error::Error; -use std::{fmt, io}; +use std::io; use thiserror::Error; /// An error related to storage operations (reads, writes...). @@ -15,8 +15,8 @@ pub enum StorageError { #[error(transparent)] Corruption(#[from] CorruptionError), #[doc(hidden)] - #[error(transparent)] - Other(Box), + #[error("{0}")] + Other(#[source] Box), } impl From for io::Error { @@ -31,52 +31,25 @@ impl From for io::Error { } /// An error return if some content in the database is corrupted. -#[derive(Debug)] -pub struct CorruptionError { - inner: CorruptionErrorKind, -} - -#[derive(Debug)] -enum CorruptionErrorKind { +#[derive(Debug, Error)] +pub enum CorruptionError { + #[error("{0}")] Msg(String), - Other(Box), + #[error("{0}")] + Other(#[source] Box), } impl CorruptionError { /// Builds an error from a printable error message. #[inline] pub(crate) fn new(error: impl Into>) -> Self { - Self { - inner: CorruptionErrorKind::Other(error.into()), - } + Self::Other(error.into()) } /// Builds an error from a printable error message. #[inline] pub(crate) fn msg(msg: impl Into) -> Self { - Self { - inner: CorruptionErrorKind::Msg(msg.into()), - } - } -} - -impl fmt::Display for CorruptionError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.inner { - CorruptionErrorKind::Msg(e) => e.fmt(f), - CorruptionErrorKind::Other(e) => e.fmt(f), - } - } -} - -impl Error for CorruptionError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.inner { - CorruptionErrorKind::Msg(_) => None, - CorruptionErrorKind::Other(e) => Some(e.as_ref()), - } + Self::Msg(msg.into()) } } diff --git a/lib/oxrdf/Cargo.toml b/lib/oxrdf/Cargo.toml index 90b2bb12..6fec2123 100644 --- a/lib/oxrdf/Cargo.toml +++ b/lib/oxrdf/Cargo.toml @@ -22,6 +22,7 @@ oxilangtag.workspace = true oxiri.workspace = true oxsdatatypes = { workspace = true, optional = true } rand.workspace = true +thiserror.workspace = true [lints] workspace = true diff --git a/lib/oxrdf/src/parser.rs b/lib/oxrdf/src/parser.rs index 1794540d..2d6f1428 100644 --- a/lib/oxrdf/src/parser.rs +++ b/lib/oxrdf/src/parser.rs @@ -5,9 +5,9 @@ use crate::{ }; #[cfg(feature = "rdf-star")] use crate::{Subject, Triple}; -use std::error::Error; +use std::char; use std::str::{Chars, FromStr}; -use std::{char, fmt}; +use thiserror::Error; /// This limit is set in order to avoid stack overflow error when parsing nested triples due to too many recursive calls. /// The actual limit value is a wet finger compromise between not failing to parse valid files and avoiding to trigger stack overflow errors. @@ -166,11 +166,9 @@ impl FromStr for Variable { "Variable serialization should start with ? or $", )); } - Self::new(&s[1..]).map_err(|error| Self::Err { - kind: TermParseErrorKind::Variable { - value: s.to_owned(), - error, - }, + Self::new(&s[1..]).map_err(|error| Self::Err::Variable { + value: s.to_owned(), + error, }) } } @@ -183,11 +181,9 @@ fn read_named_node(s: &str) -> Result<(NamedNode, &str), TermParseError> { .ok_or_else(|| TermParseError::msg("Named node serialization should end with a >"))?; let (value, remain) = remain.split_at(end); let remain = &remain[1..]; - let term = NamedNode::new(value).map_err(|error| TermParseError { - kind: TermParseErrorKind::Iri { - value: value.to_owned(), - error, - }, + let term = NamedNode::new(value).map_err(|error| TermParseError::Iri { + value: value.to_owned(), + error, })?; Ok((term, remain)) } else { @@ -207,11 +203,9 @@ fn read_blank_node(s: &str) -> Result<(BlankNode, &str), TermParseError> { }) .unwrap_or(remain.len()); let (value, remain) = remain.split_at(end); - let term = BlankNode::new(value).map_err(|error| TermParseError { - kind: TermParseErrorKind::BlankNode { - value: value.to_owned(), - error, - }, + let term = BlankNode::new(value).map_err(|error| TermParseError::BlankNode { + value: value.to_owned(), + error, })?; Ok((term, remain)) } else { @@ -237,11 +231,9 @@ fn read_literal(s: &str) -> Result<(Literal, &str), TermParseError> { let (language, remain) = remain.split_at(end); Ok(( Literal::new_language_tagged_literal(value, language).map_err( - |error| TermParseError { - kind: TermParseErrorKind::LanguageTag { - value: language.to_owned(), - error, - }, + |error| TermParseError::LanguageTag { + value: language.to_owned(), + error, }, )?, remain, @@ -421,61 +413,31 @@ fn read_hexa_char(input: &mut Chars<'_>, len: usize) -> Result) -> fmt::Result { - match &self.kind { - TermParseErrorKind::Iri { error, value } => { - write!(f, "Error while parsing the named node '{value}': {error}") - } - TermParseErrorKind::BlankNode { error, value } => { - write!(f, "Error while parsing the blank node '{value}': {error}") - } - TermParseErrorKind::LanguageTag { error, value } => { - write!(f, "Error while parsing the language tag '{value}': {error}") - } - TermParseErrorKind::Variable { error, value } => { - write!(f, "Error while parsing the variable '{value}': {error}") - } - TermParseErrorKind::Msg { msg } => f.write_str(msg), - } - } -} - -impl Error for TermParseError {} - impl TermParseError { pub(crate) fn msg(msg: &'static str) -> Self { - Self { - kind: TermParseErrorKind::Msg { msg }, - } + Self::Msg { msg } } } diff --git a/lib/oxrdfio/src/error.rs b/lib/oxrdfio/src/error.rs index 86534d46..741ff6d9 100644 --- a/lib/oxrdfio/src/error.rs +++ b/lib/oxrdfio/src/error.rs @@ -1,6 +1,5 @@ -use std::error::Error; +use std::io; use std::ops::Range; -use std::{fmt, io}; use thiserror::Error; /// Error returned during RDF format parsing. @@ -16,18 +15,7 @@ pub enum ParseError { impl ParseError { pub(crate) fn msg(msg: &'static str) -> Self { - Self::Syntax(SyntaxError { - inner: SyntaxErrorKind::Msg { msg }, - }) - } -} - -impl From for SyntaxError { - #[inline] - fn from(error: oxttl::SyntaxError) -> Self { - Self { - inner: SyntaxErrorKind::Turtle(error), - } + Self::Syntax(SyntaxError::Msg { msg }) } } @@ -41,15 +29,6 @@ impl From for ParseError { } } -impl From for SyntaxError { - #[inline] - fn from(error: oxrdfxml::SyntaxError) -> Self { - Self { - inner: SyntaxErrorKind::RdfXml(error), - } - } -} - impl From for ParseError { #[inline] fn from(error: oxrdfxml::ParseError) -> Self { @@ -71,15 +50,13 @@ impl From for io::Error { } /// An error in the syntax of the parsed file. -#[derive(Debug)] -pub struct SyntaxError { - inner: SyntaxErrorKind, -} - -#[derive(Debug)] -enum SyntaxErrorKind { - Turtle(oxttl::SyntaxError), - RdfXml(oxrdfxml::SyntaxError), +#[derive(Debug, Error)] +pub enum SyntaxError { + #[error(transparent)] + Turtle(#[from] oxttl::SyntaxError), + #[error(transparent)] + RdfXml(#[from] oxrdfxml::SyntaxError), + #[error("{msg}")] Msg { msg: &'static str }, } @@ -87,8 +64,8 @@ impl SyntaxError { /// The location of the error inside of the file. #[inline] pub fn location(&self) -> Option> { - match &self.inner { - SyntaxErrorKind::Turtle(e) => { + match &self { + Self::Turtle(e) => { let location = e.location(); Some( TextPosition { @@ -102,29 +79,7 @@ impl SyntaxError { }, ) } - SyntaxErrorKind::RdfXml(_) | SyntaxErrorKind::Msg { .. } => None, - } - } -} - -impl fmt::Display for SyntaxError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.inner { - SyntaxErrorKind::Turtle(e) => e.fmt(f), - SyntaxErrorKind::RdfXml(e) => e.fmt(f), - SyntaxErrorKind::Msg { msg } => f.write_str(msg), - } - } -} - -impl Error for SyntaxError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.inner { - SyntaxErrorKind::Turtle(e) => Some(e), - SyntaxErrorKind::RdfXml(e) => Some(e), - SyntaxErrorKind::Msg { .. } => None, + Self::RdfXml(_) | Self::Msg { .. } => None, } } } @@ -132,10 +87,10 @@ impl Error for SyntaxError { impl From for io::Error { #[inline] fn from(error: SyntaxError) -> Self { - match error.inner { - SyntaxErrorKind::Turtle(error) => error.into(), - SyntaxErrorKind::RdfXml(error) => error.into(), - SyntaxErrorKind::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), + match error { + SyntaxError::Turtle(error) => error.into(), + SyntaxError::RdfXml(error) => error.into(), + SyntaxError::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), } } } diff --git a/lib/oxrdfxml/src/error.rs b/lib/oxrdfxml/src/error.rs index 798a2f5d..3ed9312d 100644 --- a/lib/oxrdfxml/src/error.rs +++ b/lib/oxrdfxml/src/error.rs @@ -1,8 +1,7 @@ use oxilangtag::LanguageTagParseError; use oxiri::IriParseError; -use std::error::Error; +use std::io; use std::sync::Arc; -use std::{fmt, io}; use thiserror::Error; /// Error returned during RDF/XML parsing. @@ -33,78 +32,45 @@ impl From for ParseError { quick_xml::Error::Io(error) => { Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e))) } - _ => Self::Syntax(SyntaxError { - inner: SyntaxErrorKind::Xml(error), - }), + _ => Self::Syntax(SyntaxError::Xml(error)), } } } /// An error in the syntax of the parsed file. -#[derive(Debug)] -pub struct SyntaxError { - pub(crate) inner: SyntaxErrorKind, -} - -#[derive(Debug)] -pub enum SyntaxErrorKind { - Xml(quick_xml::Error), +#[derive(Debug, Error)] +pub enum SyntaxError { + #[error(transparent)] + Xml(#[from] quick_xml::Error), + #[error("error while parsing IRI '{iri}': {error}")] InvalidIri { iri: String, + #[source] error: IriParseError, }, + #[error("error while parsing language tag '{tag}': {error}")] InvalidLanguageTag { tag: String, + #[source] error: LanguageTagParseError, }, - Msg { - msg: String, - }, + #[error("{msg}")] + Msg { msg: String }, } impl SyntaxError { /// Builds an error from a printable error message. #[inline] pub(crate) fn msg(msg: impl Into) -> Self { - Self { - inner: SyntaxErrorKind::Msg { msg: msg.into() }, - } - } -} - -impl fmt::Display for SyntaxError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.inner { - SyntaxErrorKind::Xml(error) => error.fmt(f), - SyntaxErrorKind::InvalidIri { iri, error } => { - write!(f, "error while parsing IRI '{iri}': {error}") - } - SyntaxErrorKind::InvalidLanguageTag { tag, error } => { - write!(f, "error while parsing language tag '{tag}': {error}") - } - SyntaxErrorKind::Msg { msg } => f.write_str(msg), - } - } -} - -impl Error for SyntaxError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.inner { - SyntaxErrorKind::Xml(error) => Some(error), - SyntaxErrorKind::InvalidIri { error, .. } => Some(error), - SyntaxErrorKind::InvalidLanguageTag { error, .. } => Some(error), - SyntaxErrorKind::Msg { .. } => None, - } + Self::Msg { msg: msg.into() } } } impl From for io::Error { #[inline] fn from(error: SyntaxError) -> Self { - match error.inner { - SyntaxErrorKind::Xml(error) => match error { + match error { + SyntaxError::Xml(error) => match error { quick_xml::Error::Io(error) => { Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e)) } @@ -113,7 +79,7 @@ impl From for io::Error { } _ => Self::new(io::ErrorKind::InvalidData, error), }, - SyntaxErrorKind::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), + SyntaxError::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), _ => Self::new(io::ErrorKind::InvalidData, error), } } diff --git a/lib/oxrdfxml/src/parser.rs b/lib/oxrdfxml/src/parser.rs index a4e35784..0ef2adf8 100644 --- a/lib/oxrdfxml/src/parser.rs +++ b/lib/oxrdfxml/src/parser.rs @@ -1,4 +1,4 @@ -use crate::error::{ParseError, SyntaxError, SyntaxErrorKind}; +use crate::error::{ParseError, SyntaxError}; use crate::utils::*; use oxilangtag::LanguageTag; use oxiri::{Iri, IriParseError}; @@ -575,9 +575,7 @@ impl RdfXmlReader { tag } else { LanguageTag::parse(tag.to_ascii_lowercase()) - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidLanguageTag { tag, error }, - })? + .map_err(|error| SyntaxError::InvalidLanguageTag { tag, error })? .into_inner() }); } else if attribute.key.as_ref() == b"xml:base" { @@ -588,9 +586,7 @@ impl RdfXmlReader { } else { Iri::parse(iri.clone()) } - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidIri { iri, error }, - })?, + .map_err(|error| SyntaxError::InvalidIri { iri, error })?, ) } else { // We ignore other xml attributes @@ -1169,11 +1165,9 @@ impl RdfXmlReader { } else { base_iri.resolve(&relative_iri) } - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidIri { - iri: relative_iri, - error, - }, + .map_err(|error| SyntaxError::InvalidIri { + iri: relative_iri, + error, })? .into_inner(), )) @@ -1187,11 +1181,9 @@ impl RdfXmlReader { relative_iri } else { Iri::parse(relative_iri.clone()) - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidIri { - iri: relative_iri, - error, - }, + .map_err(|error| SyntaxError::InvalidIri { + iri: relative_iri, + error, })? .into_inner() })) diff --git a/lib/oxsdatatypes/Cargo.toml b/lib/oxsdatatypes/Cargo.toml index fb63c125..0598244b 100644 --- a/lib/oxsdatatypes/Cargo.toml +++ b/lib/oxsdatatypes/Cargo.toml @@ -13,6 +13,9 @@ documentation = "https://docs.rs/oxsdatatypes" edition.workspace = true rust-version.workspace = true +[dependencies] +thiserror.workspace = true + [features] js = ["js-sys"] custom-now = [] diff --git a/lib/oxsdatatypes/src/date_time.rs b/lib/oxsdatatypes/src/date_time.rs index 1d165bbb..0a22473f 100644 --- a/lib/oxsdatatypes/src/date_time.rs +++ b/lib/oxsdatatypes/src/date_time.rs @@ -6,6 +6,7 @@ use std::error::Error; use std::fmt; use std::hash::{Hash, Hasher}; use std::str::FromStr; +use thiserror::Error; /// [XML Schema `dateTime` datatype](https://www.w3.org/TR/xmlschema11-2/#dateTime) /// @@ -2039,42 +2040,24 @@ fn time_on_timeline(props: &DateTimeSevenPropertyModel) -> Option { } /// A parsing error -#[derive(Debug, Clone)] -pub struct ParseDateTimeError { - kind: ParseDateTimeErrorKind, -} - -#[derive(Debug, Clone)] -enum ParseDateTimeErrorKind { +#[derive(Debug, Clone, Error)] +pub enum ParseDateTimeError { + #[error("{day} is not a valid day of {month}")] InvalidDayOfMonth { day: u8, month: u8 }, - Overflow(DateTimeOverflowError), + #[error(transparent)] + Overflow(#[from] DateTimeOverflowError), + #[error(transparent)] InvalidTimezone(InvalidTimezoneError), + #[error("{0}")] Message(&'static str), } -impl fmt::Display for ParseDateTimeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.kind { - ParseDateTimeErrorKind::InvalidDayOfMonth { day, month } => { - write!(f, "{day} is not a valid day of {month}") - } - ParseDateTimeErrorKind::Overflow(error) => error.fmt(f), - ParseDateTimeErrorKind::InvalidTimezone(error) => error.fmt(f), - ParseDateTimeErrorKind::Message(msg) => f.write_str(msg), - } - } -} - impl ParseDateTimeError { const fn msg(message: &'static str) -> Self { - Self { - kind: ParseDateTimeErrorKind::Message(message), - } + Self::Message(message) } } -impl Error for ParseDateTimeError {} - // [16] dateTimeLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag? fn date_time_lexical_rep(input: &str) -> Result<(DateTime, &str), ParseDateTimeError> { let (year, input) = year_frag(input)?; @@ -2326,11 +2309,8 @@ fn timezone_frag(input: &str) -> Result<(TimezoneOffset, &str), ParseDateTimeErr } Ok(( - TimezoneOffset::new(sign * (hours * 60 + i16::from(minutes))).map_err(|e| { - ParseDateTimeError { - kind: ParseDateTimeErrorKind::InvalidTimezone(e), - } - })?, + TimezoneOffset::new(sign * (hours * 60 + i16::from(minutes))) + .map_err(|e| ParseDateTimeError::InvalidTimezone(e))?, input, )) } @@ -2400,9 +2380,7 @@ fn optional_end( fn validate_day_of_month(year: Option, month: u8, day: u8) -> Result<(), ParseDateTimeError> { // Constraint: Day-of-month Values if day > days_in_month(year, month) { - return Err(ParseDateTimeError { - kind: ParseDateTimeErrorKind::InvalidDayOfMonth { day, month }, - }); + return Err(ParseDateTimeError::InvalidDayOfMonth { day, month }); } Ok(()) } @@ -2421,14 +2399,6 @@ impl fmt::Display for DateTimeOverflowError { impl Error for DateTimeOverflowError {} -impl From for ParseDateTimeError { - fn from(error: DateTimeOverflowError) -> Self { - Self { - kind: ParseDateTimeErrorKind::Overflow(error), - } - } -} - /// The value provided as timezone is not valid. /// /// Matches XPath [`FODT0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0003). diff --git a/lib/oxsdatatypes/src/decimal.rs b/lib/oxsdatatypes/src/decimal.rs index ee84b604..63f1c357 100644 --- a/lib/oxsdatatypes/src/decimal.rs +++ b/lib/oxsdatatypes/src/decimal.rs @@ -3,6 +3,7 @@ use std::error::Error; use std::fmt; use std::fmt::Write; use std::str::FromStr; +use thiserror::Error; const DECIMAL_PART_DIGITS: u32 = 18; const DECIMAL_PART_POW: i128 = 1_000_000_000_000_000_000; @@ -466,7 +467,7 @@ impl FromStr for Decimal { // (\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+) let input = input.as_bytes(); if input.is_empty() { - return Err(PARSE_UNEXPECTED_END); + return Err(ParseDecimalError::UnexpectedEnd); } let (sign, mut input) = match input.first() { @@ -481,9 +482,9 @@ impl FromStr for Decimal { if c.is_ascii_digit() { value = value .checked_mul(10) - .ok_or(PARSE_OVERFLOW)? + .ok_or(ParseDecimalError::Overflow)? .checked_add(sign * i128::from(*c - b'0')) - .ok_or(PARSE_OVERFLOW)?; + .ok_or(ParseDecimalError::Overflow)?; input = &input[1..]; } else { break; @@ -493,12 +494,12 @@ impl FromStr for Decimal { let mut exp = DECIMAL_PART_POW; if let Some(c) = input.first() { if *c != b'.' { - return Err(PARSE_UNEXPECTED_CHAR); + return Err(ParseDecimalError::UnexpectedChar); } input = &input[1..]; if input.is_empty() && !with_before_dot { // We only have a dot - return Err(PARSE_UNEXPECTED_END); + return Err(ParseDecimalError::UnexpectedEnd); } while input.last() == Some(&b'0') { // Hack to avoid underflows @@ -509,25 +510,25 @@ impl FromStr for Decimal { exp /= 10; value = value .checked_mul(10) - .ok_or(PARSE_OVERFLOW)? + .ok_or(ParseDecimalError::Overflow)? .checked_add(sign * i128::from(*c - b'0')) - .ok_or(PARSE_OVERFLOW)?; + .ok_or(ParseDecimalError::Overflow)?; input = &input[1..]; } else { - return Err(PARSE_UNEXPECTED_CHAR); + return Err(ParseDecimalError::UnexpectedChar); } } if exp == 0 { // Underflow - return Err(PARSE_UNDERFLOW); + return Err(ParseDecimalError::Underflow); } } else if !with_before_dot { // It's empty - return Err(PARSE_UNEXPECTED_END); + return Err(ParseDecimalError::UnexpectedEnd); } Ok(Self { - value: value.checked_mul(exp).ok_or(PARSE_OVERFLOW)?, + value: value.checked_mul(exp).ok_or(ParseDecimalError::Overflow)?, }) } } @@ -609,50 +610,21 @@ impl fmt::Display for Decimal { } /// An error when parsing a [`Decimal`]. -#[derive(Debug, Clone)] -pub struct ParseDecimalError { - kind: DecimalParseErrorKind, -} - -#[derive(Debug, Clone)] -enum DecimalParseErrorKind { +#[derive(Debug, Clone, Error)] +pub enum ParseDecimalError { + #[error("Value overflow")] Overflow, + #[error("Value underflow")] Underflow, + #[error("Unexpected character")] UnexpectedChar, + #[error("Unexpected end of string")] UnexpectedEnd, } -const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError { - kind: DecimalParseErrorKind::Overflow, -}; -const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError { - kind: DecimalParseErrorKind::Underflow, -}; -const PARSE_UNEXPECTED_CHAR: ParseDecimalError = ParseDecimalError { - kind: DecimalParseErrorKind::UnexpectedChar, -}; -const PARSE_UNEXPECTED_END: ParseDecimalError = ParseDecimalError { - kind: DecimalParseErrorKind::UnexpectedEnd, -}; - -impl fmt::Display for ParseDecimalError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - DecimalParseErrorKind::Overflow => f.write_str("Value overflow"), - DecimalParseErrorKind::Underflow => f.write_str("Value underflow"), - DecimalParseErrorKind::UnexpectedChar => f.write_str("Unexpected character"), - DecimalParseErrorKind::UnexpectedEnd => f.write_str("Unexpected end of string"), - } - } -} - -impl Error for ParseDecimalError {} - impl From for ParseDecimalError { fn from(_: TooLargeForDecimalError) -> Self { - Self { - kind: DecimalParseErrorKind::Overflow, - } + Self::Overflow } } diff --git a/lib/sparesults/src/csv.rs b/lib/sparesults/src/csv.rs index bd2fe4b1..7d975bd8 100644 --- a/lib/sparesults/src/csv.rs +++ b/lib/sparesults/src/csv.rs @@ -1,6 +1,6 @@ //! Implementation of [SPARQL 1.1 Query Results CSV and TSV Formats](https://www.w3.org/TR/sparql11-results-csv-tsv/) -use crate::error::{ParseError, SyntaxError, SyntaxErrorKind, TextPosition}; +use crate::error::{ParseError, SyntaxError, TextPosition}; use memchr::memchr; use oxrdf::vocab::xsd; use oxrdf::*; @@ -508,23 +508,21 @@ impl TsvSolutionsReader { .sum::(); let start_position_bytes = line.split('\t').take(i).map(|c| c.len() + 1).sum::(); - SyntaxError { - inner: SyntaxErrorKind::Term { - error: e, - term: v.into(), - location: TextPosition { - line: self.reader.line_count - 1, - column: start_position_char.try_into().unwrap(), - offset: self.reader.last_line_start - + u64::try_from(start_position_bytes).unwrap(), - }..TextPosition { - line: self.reader.line_count - 1, - column: (start_position_char + v.chars().count()) - .try_into() - .unwrap(), - offset: self.reader.last_line_start - + u64::try_from(start_position_bytes + v.len()).unwrap(), - }, + SyntaxError::Term { + error: e, + term: v.into(), + location: TextPosition { + line: self.reader.line_count - 1, + column: start_position_char.try_into().unwrap(), + offset: self.reader.last_line_start + + u64::try_from(start_position_bytes).unwrap(), + }..TextPosition { + line: self.reader.line_count - 1, + column: (start_position_char + v.chars().count()) + .try_into() + .unwrap(), + offset: self.reader.last_line_start + + u64::try_from(start_position_bytes + v.len()).unwrap(), }, } })?)) diff --git a/lib/sparesults/src/error.rs b/lib/sparesults/src/error.rs index 6575a2ea..31c55ea2 100644 --- a/lib/sparesults/src/error.rs +++ b/lib/sparesults/src/error.rs @@ -1,8 +1,7 @@ use oxrdf::TermParseError; -use std::error::Error; +use std::io; use std::ops::Range; use std::sync::Arc; -use std::{fmt, io}; use thiserror::Error; /// Error returned during SPARQL result formats format parsing. @@ -42,28 +41,26 @@ impl From for ParseError { quick_xml::Error::Io(error) => { Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e))) } - _ => Self::Syntax(SyntaxError { - inner: SyntaxErrorKind::Xml(error), - }), + _ => Self::Syntax(SyntaxError::Xml(error)), } } } /// An error in the syntax of the parsed file. -#[derive(Debug)] -pub struct SyntaxError { - pub(crate) inner: SyntaxErrorKind, -} - -#[derive(Debug)] -pub(crate) enum SyntaxErrorKind { - Json(json_event_parser::SyntaxError), - Xml(quick_xml::Error), +#[derive(Debug, Error)] +pub enum SyntaxError { + #[error(transparent)] + Json(#[from] json_event_parser::SyntaxError), + #[error(transparent)] + Xml(#[from] quick_xml::Error), + #[error("Error {error} on '{term}' in line {}", location.start.line + 1)] Term { + #[source] error: TermParseError, term: String, location: Range, }, + #[error("{msg}")] Msg { msg: String, location: Option>, @@ -74,30 +71,26 @@ impl SyntaxError { /// Builds an error from a printable error message. #[inline] pub(crate) fn msg(msg: impl Into) -> Self { - Self { - inner: SyntaxErrorKind::Msg { - msg: msg.into(), - location: None, - }, + Self::Msg { + msg: msg.into(), + location: None, } } /// Builds an error from a printable error message and a location #[inline] pub(crate) fn located_message(msg: impl Into, location: Range) -> Self { - Self { - inner: SyntaxErrorKind::Msg { - msg: msg.into(), - location: Some(location), - }, + Self::Msg { + msg: msg.into(), + location: Some(location), } } /// The location of the error inside of the file. #[inline] pub fn location(&self) -> Option> { - match &self.inner { - SyntaxErrorKind::Json(e) => { + match self { + Self::Json(e) => { let location = e.location(); Some( TextPosition { @@ -111,37 +104,9 @@ impl SyntaxError { }, ) } - SyntaxErrorKind::Term { location, .. } => Some(location.clone()), - SyntaxErrorKind::Msg { location, .. } => location.clone(), - SyntaxErrorKind::Xml(_) => None, - } - } -} - -impl fmt::Display for SyntaxError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.inner { - SyntaxErrorKind::Json(e) => e.fmt(f), - SyntaxErrorKind::Xml(e) => e.fmt(f), - SyntaxErrorKind::Term { - error, - term, - location, - } => write!(f, "{error} on '{term}' in line {}", location.start.line + 1), - SyntaxErrorKind::Msg { msg, .. } => f.write_str(msg), - } - } -} - -impl Error for SyntaxError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.inner { - SyntaxErrorKind::Json(e) => Some(e), - SyntaxErrorKind::Xml(e) => Some(e), - SyntaxErrorKind::Term { error, .. } => Some(error), - SyntaxErrorKind::Msg { .. } => None, + Self::Term { location, .. } => Some(location.clone()), + Self::Msg { location, .. } => location.clone(), + Self::Xml(_) => None, } } } @@ -149,9 +114,9 @@ impl Error for SyntaxError { impl From for io::Error { #[inline] fn from(error: SyntaxError) -> Self { - match error.inner { - SyntaxErrorKind::Json(error) => Self::new(io::ErrorKind::InvalidData, error), - SyntaxErrorKind::Xml(error) => match error { + match error { + SyntaxError::Json(error) => Self::new(io::ErrorKind::InvalidData, error), + SyntaxError::Xml(error) => match error { quick_xml::Error::Io(error) => { Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e)) } @@ -160,16 +125,8 @@ impl From for io::Error { } _ => Self::new(io::ErrorKind::InvalidData, error), }, - SyntaxErrorKind::Term { .. } => Self::new(io::ErrorKind::InvalidData, error), - SyntaxErrorKind::Msg { msg, .. } => Self::new(io::ErrorKind::InvalidData, msg), - } - } -} - -impl From for SyntaxError { - fn from(error: json_event_parser::SyntaxError) -> Self { - Self { - inner: SyntaxErrorKind::Json(error), + SyntaxError::Term { .. } => Self::new(io::ErrorKind::InvalidData, error), + SyntaxError::Msg { msg, .. } => Self::new(io::ErrorKind::InvalidData, msg), } } } diff --git a/lib/spargebra/Cargo.toml b/lib/spargebra/Cargo.toml index 24acb482..f367c57c 100644 --- a/lib/spargebra/Cargo.toml +++ b/lib/spargebra/Cargo.toml @@ -25,6 +25,7 @@ oxiri.workspace = true oxrdf.workspace = true peg.workspace = true rand.workspace = true +thiserror.workspace = true [lints] workspace = true diff --git a/lib/spargebra/src/parser.rs b/lib/spargebra/src/parser.rs index bcb4d491..ff67818d 100644 --- a/lib/spargebra/src/parser.rs +++ b/lib/spargebra/src/parser.rs @@ -9,26 +9,22 @@ use oxrdf::vocab::{rdf, xsd}; use peg::parser; use peg::str::LineCol; use rand::random; +use std::char; use std::collections::{HashMap, HashSet}; -use std::error::Error; use std::mem::take; use std::str::FromStr; -use std::{char, fmt}; +use thiserror::Error; /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query. pub fn parse_query(query: &str, base_iri: Option<&str>) -> Result { let mut state = ParserState::from_base_iri(base_iri)?; - parser::QueryUnit(query, &mut state).map_err(|e| ParseError { - inner: ParseErrorKind::Parser(e), - }) + parser::QueryUnit(query, &mut state).map_err(|e| ParseError::Parser(e)) } /// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query. pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result { let mut state = ParserState::from_base_iri(base_iri)?; - let operations = parser::UpdateInit(update, &mut state).map_err(|e| ParseError { - inner: ParseErrorKind::Parser(e), - })?; + let operations = parser::UpdateInit(update, &mut state).map_err(|e| ParseError::Parser(e))?; Ok(Update { operations, base_iri: state.base_iri, @@ -36,37 +32,12 @@ pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result), -} - -impl fmt::Display for ParseError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.inner { - ParseErrorKind::InvalidBaseIri(e) => { - write!(f, "Invalid SPARQL base IRI provided: {e}") - } - ParseErrorKind::Parser(e) => e.fmt(f), - } - } -} - -impl Error for ParseError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.inner { - ParseErrorKind::InvalidBaseIri(e) => Some(e), - ParseErrorKind::Parser(e) => Some(e), - } - } +#[derive(Debug, Error)] +pub enum ParseError { + #[error("Invalid SPARQL base IRI provided: {0}")] + InvalidBaseIri(#[from] IriParseError), + #[error(transparent)] + Parser(#[from] peg::error::ParseError), } struct AnnotatedTerm { @@ -697,9 +668,7 @@ impl ParserState { pub(crate) fn from_base_iri(base_iri: Option<&str>) -> Result { Ok(Self { base_iri: if let Some(base_iri) = base_iri { - Some(Iri::parse(base_iri.to_owned()).map_err(|e| ParseError { - inner: ParseErrorKind::InvalidBaseIri(e), - })?) + Some(Iri::parse(base_iri.to_owned()).map_err(|e| ParseError::InvalidBaseIri(e))?) } else { None },