From 655ecd3e91f21ef8ab9ce06e91015c7af85f797e Mon Sep 17 00:00:00 2001 From: Tpt Date: Sat, 10 Feb 2024 17:20:23 +0100 Subject: [PATCH] Convert error to thiserror Co-authored-by: Yuri Astrakhan --- Cargo.lock | 31 ++-- Cargo.toml | 2 +- lib/oxigraph/src/sparql/error.rs | 110 +++----------- lib/oxigraph/src/storage/backend/fallback.rs | 2 +- lib/oxigraph/src/storage/backend/rocksdb.rs | 12 +- lib/oxigraph/src/storage/error.rs | 146 +++++-------------- lib/oxigraph/src/storage/numeric_encoder.rs | 30 +--- lib/oxigraph/src/storage/small_string.rs | 30 +--- lib/oxrdf/Cargo.toml | 1 + lib/oxrdf/src/blank_node.rs | 13 +- lib/oxrdf/src/parser.rs | 78 ++++------ lib/oxrdf/src/variable.rs | 13 +- lib/oxrdfio/Cargo.toml | 1 + lib/oxrdfio/src/error.rs | 106 +++----------- lib/oxrdfxml/Cargo.toml | 1 + lib/oxrdfxml/src/error.rs | 106 +++----------- lib/oxrdfxml/src/parser.rs | 20 ++- lib/oxsdatatypes/Cargo.toml | 3 + lib/oxsdatatypes/src/date_time.rs | 82 +++-------- lib/oxsdatatypes/src/decimal.rs | 60 +++----- lib/oxsdatatypes/src/duration.rs | 35 +---- lib/oxsdatatypes/src/integer.rs | 12 +- lib/oxttl/Cargo.toml | 1 + lib/oxttl/src/toolkit/error.rs | 47 +----- lib/sparesults/Cargo.toml | 1 + lib/sparesults/src/error.rs | 88 +++-------- lib/spargebra/Cargo.toml | 1 + lib/spargebra/src/parser.rs | 56 ++----- 28 files changed, 273 insertions(+), 815 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d729862c..85d35123 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", @@ -312,9 +312,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "codspeed" @@ -786,9 +786,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] @@ -1136,6 +1136,7 @@ dependencies = [ "oxiri", "oxsdatatypes", "rand", + "thiserror", ] [[package]] @@ -1145,6 +1146,7 @@ dependencies = [ "oxrdf", "oxrdfxml", "oxttl", + "thiserror", "tokio", ] @@ -1156,6 +1158,7 @@ dependencies = [ "oxiri", "oxrdf", "quick-xml", + "thiserror", "tokio", ] @@ -1174,6 +1177,7 @@ name = "oxsdatatypes" version = "0.2.0-alpha.1" dependencies = [ "js-sys", + "thiserror", ] [[package]] @@ -1184,6 +1188,7 @@ dependencies = [ "oxilangtag", "oxiri", "oxrdf", + "thiserror", "tokio", ] @@ -1745,6 +1750,7 @@ dependencies = [ "memchr", "oxrdf", "quick-xml", + "thiserror", "tokio", ] @@ -1757,6 +1763,7 @@ dependencies = [ "oxrdf", "peg", "rand", + "thiserror", ] [[package]] @@ -1783,9 +1790,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "subtle" diff --git a/Cargo.toml b/Cargo.toml index 1c0cc9e6..203ae7a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ sha1 = "0.10" sha2 = "0.10" siphasher = ">=0.3, <2.0" text-diff = "0.4" -thiserror = "1" +thiserror = "1.0.50" time = "0.3" tokio = "1.29" url = "2.4" diff --git a/lib/oxigraph/src/sparql/error.rs b/lib/oxigraph/src/sparql/error.rs index b3516d8e..94ee6f57 100644 --- a/lib/oxigraph/src/sparql/error.rs +++ b/lib/oxigraph/src/sparql/error.rs @@ -5,93 +5,53 @@ use crate::sparql::ParseError; use crate::storage::StorageError; use std::convert::Infallible; use std::error::Error; -use std::{fmt, io}; +use std::io; /// A SPARQL evaluation error. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum EvaluationError { /// An error in SPARQL parsing. - Parsing(ParseError), + #[error(transparent)] + Parsing(#[from] ParseError), /// An error from the storage. - Storage(StorageError), + #[error(transparent)] + Storage(#[from] StorageError), /// An error while parsing an external RDF file. - GraphParsing(RdfParseError), + #[error(transparent)] + GraphParsing(#[from] RdfParseError), /// An error while parsing an external result file (likely from a federated query). - ResultsParsing(ResultsParseError), + #[error(transparent)] + ResultsParsing(#[from] ResultsParseError), /// An error returned during results serialization. - ResultsSerialization(io::Error), + #[error(transparent)] + ResultsSerialization(#[from] io::Error), /// Error during `SERVICE` evaluation - Service(Box), + #[error("{0}")] + Service(#[source] Box), /// Error when `CREATE` tries to create an already existing graph + #[error("The graph {0} already exists")] GraphAlreadyExists(NamedNode), /// Error when `DROP` or `CLEAR` tries to remove a not existing graph + #[error("The graph {0} does not exist")] GraphDoesNotExist(NamedNode), /// The variable storing the `SERVICE` name is unbound + #[error("The variable encoding the service name is unbound")] UnboundService, /// The given `SERVICE` is not supported + #[error("The service {0} is not supported")] UnsupportedService(NamedNode), /// The given content media type returned from an HTTP response is not supported (`SERVICE` and `LOAD`) + #[error("The content media type {0} is not supported")] UnsupportedContentType(String), /// The `SERVICE` call has not returns solutions + #[error("The service is not returning solutions but a boolean or a graph")] ServiceDoesNotReturnSolutions, /// The results are not a RDF graph + #[error("The query results are not a RDF graph")] NotAGraph, } -impl fmt::Display for EvaluationError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Parsing(error) => error.fmt(f), - Self::Storage(error) => error.fmt(f), - Self::GraphParsing(error) => error.fmt(f), - Self::ResultsParsing(error) => error.fmt(f), - Self::ResultsSerialization(error) => error.fmt(f), - Self::Service(error) => error.fmt(f), - Self::GraphAlreadyExists(graph) => write!(f, "The graph {graph} already exists"), - Self::GraphDoesNotExist(graph) => write!(f, "The graph {graph} does not exist"), - Self::UnboundService => { - f.write_str("The variable encoding the service name is unbound") - } - Self::UnsupportedService(service) => { - write!(f, "The service {service} is not supported") - } - Self::UnsupportedContentType(content_type) => { - write!(f, "The content media type {content_type} is not supported") - } - Self::ServiceDoesNotReturnSolutions => { - f.write_str("The service is not returning solutions but a boolean or a graph") - } - Self::NotAGraph => f.write_str("The query results are not a RDF graph"), - } - } -} - -impl Error for EvaluationError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Parsing(e) => Some(e), - Self::Storage(e) => Some(e), - Self::GraphParsing(e) => Some(e), - Self::ResultsParsing(e) => Some(e), - Self::ResultsSerialization(e) => Some(e), - Self::Service(e) => { - let e = Box::as_ref(e); - Some(e) - } - Self::GraphAlreadyExists(_) - | Self::GraphDoesNotExist(_) - | Self::UnboundService - | Self::UnsupportedService(_) - | Self::UnsupportedContentType(_) - | Self::ServiceDoesNotReturnSolutions - | Self::NotAGraph => None, - } - } -} - impl From for EvaluationError { #[inline] fn from(error: Infallible) -> Self { @@ -99,34 +59,6 @@ impl From for EvaluationError { } } -impl From for EvaluationError { - #[inline] - fn from(error: ParseError) -> Self { - Self::Parsing(error) - } -} - -impl From for EvaluationError { - #[inline] - fn from(error: StorageError) -> Self { - Self::Storage(error) - } -} - -impl From for EvaluationError { - #[inline] - fn from(error: RdfParseError) -> Self { - Self::GraphParsing(error) - } -} - -impl From for EvaluationError { - #[inline] - fn from(error: ResultsParseError) -> Self { - Self::ResultsParsing(error) - } -} - impl From for io::Error { #[inline] fn from(error: EvaluationError) -> Self { diff --git a/lib/oxigraph/src/storage/backend/fallback.rs b/lib/oxigraph/src/storage/backend/fallback.rs index c2eb17ec..7214851e 100644 --- a/lib/oxigraph/src/storage/backend/fallback.rs +++ b/lib/oxigraph/src/storage/backend/fallback.rs @@ -36,7 +36,7 @@ impl Db { if self.0.read().unwrap().contains_key(&column_family) { Ok(column_family) } else { - Err(CorruptionError::msg(format!("Column family {name} does not exist")).into()) + Err(CorruptionError::from_missing_column_family_name(name).into()) } } diff --git a/lib/oxigraph/src/storage/backend/rocksdb.rs b/lib/oxigraph/src/storage/backend/rocksdb.rs index fed85421..14c45d0a 100644 --- a/lib/oxigraph/src/storage/backend/rocksdb.rs +++ b/lib/oxigraph/src/storage/backend/rocksdb.rs @@ -548,7 +548,7 @@ impl Db { return Ok(ColumnFamily(*cf_handle)); } } - Err(CorruptionError::msg(format!("Column family {name} does not exist")).into()) + Err(CorruptionError::from_missing_column_family_name(name).into()) } #[must_use] @@ -1314,6 +1314,8 @@ impl SstFileWriter { } } +#[derive(thiserror::Error)] +#[error("{}", self.message())] struct ErrorStatus(rocksdb_status_t); unsafe impl Send for ErrorStatus {} @@ -1352,14 +1354,6 @@ impl fmt::Debug for ErrorStatus { } } -impl fmt::Display for ErrorStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.message()) - } -} - -impl Error for ErrorStatus {} - impl From for StorageError { fn from(status: ErrorStatus) -> Self { if status.0.code == rocksdb_status_code_t_rocksdb_status_code_io_error { diff --git a/lib/oxigraph/src/storage/error.rs b/lib/oxigraph/src/storage/error.rs index d0a67522..f2f27cdb 100644 --- a/lib/oxigraph/src/storage/error.rs +++ b/lib/oxigraph/src/storage/error.rs @@ -1,48 +1,23 @@ use crate::io::{ParseError, RdfFormat}; +use crate::storage::numeric_encoder::EncodedTerm; use oxiri::IriParseError; +use oxrdf::TermRef; use std::error::Error; -use std::{fmt, io}; -use thiserror::Error; +use std::io; /// An error related to storage operations (reads, writes...). -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum StorageError { /// Error from the OS I/O layer. - Io(io::Error), + #[error(transparent)] + Io(#[from] io::Error), /// Error related to data corruption. - Corruption(CorruptionError), + #[error(transparent)] + Corruption(#[from] CorruptionError), #[doc(hidden)] - Other(Box), -} - -impl fmt::Display for StorageError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(e) => e.fmt(f), - Self::Corruption(e) => e.fmt(f), - Self::Other(e) => e.fmt(f), - } - } -} - -impl Error for StorageError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Io(e) => Some(e), - Self::Corruption(e) => Some(e), - Self::Other(e) => Some(e.as_ref()), - } - } -} - -impl From for StorageError { - #[inline] - fn from(error: io::Error) -> Self { - Self::Io(error) - } + #[error("{0}")] + Other(#[source] Box), } impl From for io::Error { @@ -57,59 +32,42 @@ 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, thiserror::Error)] +#[error(transparent)] +pub struct CorruptionError(#[from] CorruptionErrorKind); -#[derive(Debug)] +/// An error return if some content in the database is corrupted. +#[derive(Debug, thiserror::Error)] enum CorruptionErrorKind { + #[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()), - } - } - - /// Builds an error from a printable error message. - #[inline] - pub(crate) fn msg(msg: impl Into) -> Self { - Self { - inner: CorruptionErrorKind::Msg(msg.into()), - } + Self(CorruptionErrorKind::Other(error.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), - } + pub(crate) fn from_encoded_term(encoded: &EncodedTerm, term: &TermRef<'_>) -> Self { + // TODO: eventually use a dedicated error enum value + Self::msg(format!("Invalid term encoding {encoded:?} for {term}")) } -} -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()), - } + pub(crate) fn from_missing_column_family_name(name: &'static str) -> Self { + // TODO: eventually use a dedicated error enum value + Self::msg(format!("Column family {name} does not exist")) } -} -impl From for StorageError { + /// Builds an error from a printable error message. #[inline] - fn from(error: CorruptionError) -> Self { - Self::Corruption(error) + pub(crate) fn msg(msg: impl Into) -> Self { + Self(CorruptionErrorKind::Msg(msg.into())) } } @@ -121,57 +79,25 @@ impl From for io::Error { } /// An error raised while loading a file into a [`Store`](crate::store::Store). -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum LoaderError { /// An error raised while reading the file. - Parsing(ParseError), + #[error(transparent)] + Parsing(#[from] ParseError), /// An error raised during the insertion in the store. - Storage(StorageError), + #[error(transparent)] + Storage(#[from] StorageError), /// The base IRI is invalid. + #[error("Invalid base IRI '{iri}': {error}")] InvalidBaseIri { /// The IRI itself. iri: String, /// The parsing error. + #[source] error: IriParseError, }, } -impl fmt::Display for LoaderError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Parsing(e) => e.fmt(f), - Self::Storage(e) => e.fmt(f), - Self::InvalidBaseIri { iri, error } => write!(f, "Invalid base IRI '{iri}': {error}"), - } - } -} - -impl Error for LoaderError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Parsing(e) => Some(e), - Self::Storage(e) => Some(e), - Self::InvalidBaseIri { error, .. } => Some(error), - } - } -} - -impl From for LoaderError { - #[inline] - fn from(error: ParseError) -> Self { - Self::Parsing(error) - } -} - -impl From for LoaderError { - #[inline] - fn from(error: StorageError) -> Self { - Self::Storage(error) - } -} - impl From for io::Error { #[inline] fn from(error: LoaderError) -> Self { @@ -186,7 +112,7 @@ impl From for io::Error { } /// An error raised while writing a file from a [`Store`](crate::store::Store). -#[derive(Debug, Error)] +#[derive(Debug, thiserror::Error)] pub enum SerializerError { /// An error raised while writing the content. #[error(transparent)] diff --git a/lib/oxigraph/src/storage/numeric_encoder.rs b/lib/oxigraph/src/storage/numeric_encoder.rs index e730a163..bf4b070b 100644 --- a/lib/oxigraph/src/storage/numeric_encoder.rs +++ b/lib/oxigraph/src/storage/numeric_encoder.rs @@ -713,19 +713,13 @@ pub fn insert_term Result<(), StorageError>>( if let EncodedTerm::NamedNode { iri_id } = encoded { insert_str(iri_id, node.as_str()) } else { - Err( - CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}")) - .into(), - ) + Err(CorruptionError::from_encoded_term(encoded, &term).into()) } } TermRef::BlankNode(node) => match encoded { EncodedTerm::BigBlankNode { id_id } => insert_str(id_id, node.as_str()), EncodedTerm::SmallBlankNode(..) | EncodedTerm::NumericalBlankNode { .. } => Ok(()), - _ => Err( - CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}")) - .into(), - ), + _ => Err(CorruptionError::from_encoded_term(encoded, &term).into()), }, TermRef::Literal(literal) => match encoded { EncodedTerm::BigStringLiteral { value_id } @@ -736,10 +730,7 @@ pub fn insert_term Result<(), StorageError>>( if let Some(language) = literal.language() { insert_str(language_id, language) } else { - Err(CorruptionError::new(format!( - "Invalid term encoding {encoded:?} for {term}" - )) - .into()) + Err(CorruptionError::from_encoded_term(encoded, &term).into()) } } EncodedTerm::BigBigLangStringLiteral { @@ -750,10 +741,7 @@ pub fn insert_term Result<(), StorageError>>( if let Some(language) = literal.language() { insert_str(language_id, language) } else { - Err(CorruptionError::new(format!( - "Invalid term encoding {encoded:?} for {term}" - )) - .into()) + Err(CorruptionError::from_encoded_term(encoded, &term).into()) } } EncodedTerm::SmallTypedLiteral { datatype_id, .. } => { @@ -784,10 +772,7 @@ pub fn insert_term Result<(), StorageError>>( | EncodedTerm::DurationLiteral(..) | EncodedTerm::YearMonthDurationLiteral(..) | EncodedTerm::DayTimeDurationLiteral(..) => Ok(()), - _ => Err( - CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}")) - .into(), - ), + _ => Err(CorruptionError::from_encoded_term(encoded, &term).into()), }, TermRef::Triple(triple) => { if let EncodedTerm::Triple(encoded) = encoded { @@ -799,10 +784,7 @@ pub fn insert_term Result<(), StorageError>>( )?; insert_term(triple.object.as_ref(), &encoded.object, insert_str) } else { - Err( - CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}")) - .into(), - ) + Err(CorruptionError::from_encoded_term(encoded, &term).into()) } } } diff --git a/lib/oxigraph/src/storage/small_string.rs b/lib/oxigraph/src/storage/small_string.rs index fcd9b227..355606da 100644 --- a/lib/oxigraph/src/storage/small_string.rs +++ b/lib/oxigraph/src/storage/small_string.rs @@ -1,6 +1,5 @@ use std::borrow::Borrow; use std::cmp::Ordering; -use std::error::Error; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::str::{FromStr, Utf8Error}; @@ -169,31 +168,10 @@ impl<'a> TryFrom<&'a str> for SmallString { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, thiserror::Error)] pub enum BadSmallStringError { + #[error("small strings could only contain at most 15 characters, found {0}")] TooLong(usize), - BadUtf8(Utf8Error), -} - -impl fmt::Display for BadSmallStringError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::TooLong(v) => write!( - f, - "small strings could only contain at most 15 characters, found {v}" - ), - Self::BadUtf8(e) => e.fmt(f), - } - } -} - -impl Error for BadSmallStringError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::TooLong(_) => None, - Self::BadUtf8(e) => Some(e), - } - } + #[error(transparent)] + BadUtf8(#[from] Utf8Error), } 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/blank_node.rs b/lib/oxrdf/src/blank_node.rs index ce07226b..6d565fda 100644 --- a/lib/oxrdf/src/blank_node.rs +++ b/lib/oxrdf/src/blank_node.rs @@ -1,5 +1,4 @@ use rand::random; -use std::error::Error; use std::io::Write; use std::{fmt, str}; @@ -345,18 +344,10 @@ fn to_integer_id(id: &str) -> Option { } /// An error raised during [`BlankNode`] IDs validation. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] +#[error("The blank node identifier is invalid")] pub struct BlankNodeIdParseError; -impl fmt::Display for BlankNodeIdParseError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("The blank node identifier is invalid") - } -} - -impl Error for BlankNodeIdParseError {} - #[cfg(test)] mod tests { #![allow(clippy::panic_in_result_fn)] diff --git a/lib/oxrdf/src/parser.rs b/lib/oxrdf/src/parser.rs index 1794540d..a531a86e 100644 --- a/lib/oxrdf/src/parser.rs +++ b/lib/oxrdf/src/parser.rs @@ -5,9 +5,8 @@ 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}; /// 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 +165,11 @@ impl FromStr for Variable { "Variable serialization should start with ? or $", )); } - Self::new(&s[1..]).map_err(|error| Self::Err { - kind: TermParseErrorKind::Variable { + Self::new(&s[1..]).map_err(|error| { + TermParseError(TermParseErrorKind::Variable { value: s.to_owned(), error, - }, + }) }) } } @@ -183,11 +182,11 @@ 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 { + let term = NamedNode::new(value).map_err(|error| { + TermParseError(TermParseErrorKind::Iri { value: value.to_owned(), error, - }, + }) })?; Ok((term, remain)) } else { @@ -207,11 +206,11 @@ 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 { + let term = BlankNode::new(value).map_err(|error| { + TermParseError(TermParseErrorKind::BlankNode { value: value.to_owned(), error, - }, + }) })?; Ok((term, remain)) } else { @@ -237,11 +236,11 @@ 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 { + |error| { + TermParseError(TermParseErrorKind::LanguageTag { value: language.to_owned(), error, - }, + }) }, )?, remain, @@ -421,61 +420,36 @@ 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(TermParseErrorKind::Msg(msg)) } } diff --git a/lib/oxrdf/src/variable.rs b/lib/oxrdf/src/variable.rs index 30af6c0f..36dcb3e4 100644 --- a/lib/oxrdf/src/variable.rs +++ b/lib/oxrdf/src/variable.rs @@ -1,5 +1,4 @@ use std::cmp::Ordering; -use std::error::Error; use std::fmt; /// A [SPARQL query](https://www.w3.org/TR/sparql11-query/) owned variable. @@ -212,14 +211,6 @@ fn validate_variable_identifier(id: &str) -> Result<(), VariableNameParseError> } /// An error raised during [`Variable`] name validation. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] +#[error("The variable name is invalid")] pub struct VariableNameParseError; - -impl fmt::Display for VariableNameParseError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("The variable name is invalid") - } -} - -impl Error for VariableNameParseError {} diff --git a/lib/oxrdfio/Cargo.toml b/lib/oxrdfio/Cargo.toml index 84e398f1..05069df3 100644 --- a/lib/oxrdfio/Cargo.toml +++ b/lib/oxrdfio/Cargo.toml @@ -22,6 +22,7 @@ rdf-star = ["oxrdf/rdf-star", "oxttl/rdf-star"] oxrdf.workspace = true oxrdfxml.workspace = true oxttl.workspace = true +thiserror.workspace = true tokio = { workspace = true, optional = true, features = ["io-util"] } [dev-dependencies] diff --git a/lib/oxrdfio/src/error.rs b/lib/oxrdfio/src/error.rs index 78f9b998..cd2c4449 100644 --- a/lib/oxrdfio/src/error.rs +++ b/lib/oxrdfio/src/error.rs @@ -1,50 +1,27 @@ -use std::error::Error; +use std::io; use std::ops::Range; -use std::{fmt, io}; /// Error returned during RDF format parsing. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum ParseError { /// I/O error during parsing (file not found...). - Io(io::Error), + #[error(transparent)] + Io(#[from] io::Error), /// An error in the file syntax. - Syntax(SyntaxError), + #[error(transparent)] + Syntax(#[from] SyntaxError), } impl ParseError { pub(crate) fn msg(msg: &'static str) -> Self { - Self::Syntax(SyntaxError { - inner: SyntaxErrorKind::Msg { msg }, - }) - } -} - -impl fmt::Display for ParseError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(e) => e.fmt(f), - Self::Syntax(e) => e.fmt(f), - } - } -} - -impl Error for ParseError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Io(e) => Some(e), - Self::Syntax(e) => Some(e), - } + Self::Syntax(SyntaxError(SyntaxErrorKind::Msg(msg))) } } impl From for SyntaxError { #[inline] fn from(error: oxttl::SyntaxError) -> Self { - Self { - inner: SyntaxErrorKind::Turtle(error), - } + Self(SyntaxErrorKind::Turtle(error)) } } @@ -61,9 +38,7 @@ impl From for ParseError { impl From for SyntaxError { #[inline] fn from(error: oxrdfxml::SyntaxError) -> Self { - Self { - inner: SyntaxErrorKind::RdfXml(error), - } + Self(SyntaxErrorKind::RdfXml(error)) } } @@ -77,20 +52,6 @@ impl From for ParseError { } } -impl From for ParseError { - #[inline] - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -impl From for ParseError { - #[inline] - fn from(error: SyntaxError) -> Self { - Self::Syntax(error) - } -} - impl From for io::Error { #[inline] fn from(error: ParseError) -> Self { @@ -102,23 +63,26 @@ impl From for io::Error { } /// An error in the syntax of the parsed file. -#[derive(Debug)] -pub struct SyntaxError { - inner: SyntaxErrorKind, -} +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct SyntaxError(#[from] SyntaxErrorKind); -#[derive(Debug)] +/// An error in the syntax of the parsed file. +#[derive(Debug, thiserror::Error)] enum SyntaxErrorKind { - Turtle(oxttl::SyntaxError), - RdfXml(oxrdfxml::SyntaxError), - Msg { msg: &'static str }, + #[error(transparent)] + Turtle(#[from] oxttl::SyntaxError), + #[error(transparent)] + RdfXml(#[from] oxrdfxml::SyntaxError), + #[error("{0}")] + Msg(&'static str), } impl SyntaxError { /// The location of the error inside of the file. #[inline] pub fn location(&self) -> Option> { - match &self.inner { + match &self.0 { SyntaxErrorKind::Turtle(e) => { let location = e.location(); Some( @@ -133,29 +97,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, + SyntaxErrorKind::RdfXml(_) | SyntaxErrorKind::Msg(_) => None, } } } @@ -163,10 +105,10 @@ impl Error for SyntaxError { impl From for io::Error { #[inline] fn from(error: SyntaxError) -> Self { - match error.inner { + match error.0 { SyntaxErrorKind::Turtle(error) => error.into(), SyntaxErrorKind::RdfXml(error) => error.into(), - SyntaxErrorKind::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), + SyntaxErrorKind::Msg(msg) => Self::new(io::ErrorKind::InvalidData, msg), } } } diff --git a/lib/oxrdfxml/Cargo.toml b/lib/oxrdfxml/Cargo.toml index 2ed9b248..efc71e62 100644 --- a/lib/oxrdfxml/Cargo.toml +++ b/lib/oxrdfxml/Cargo.toml @@ -22,6 +22,7 @@ oxilangtag.workspace = true oxiri.workspace = true oxrdf.workspace = true quick-xml.workspace = true +thiserror.workspace = true tokio = { workspace = true, optional = true, features = ["io-util"] } [dev-dependencies] diff --git a/lib/oxrdfxml/src/error.rs b/lib/oxrdfxml/src/error.rs index be2e161d..abc7c55c 100644 --- a/lib/oxrdfxml/src/error.rs +++ b/lib/oxrdfxml/src/error.rs @@ -1,50 +1,17 @@ use oxilangtag::LanguageTagParseError; use oxiri::IriParseError; -use std::error::Error; +use std::io; use std::sync::Arc; -use std::{fmt, io}; /// Error returned during RDF/XML parsing. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum ParseError { /// I/O error during parsing (file not found...). - Io(io::Error), + #[error(transparent)] + Io(#[from] io::Error), /// An error in the file syntax. - Syntax(SyntaxError), -} - -impl fmt::Display for ParseError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(e) => e.fmt(f), - Self::Syntax(e) => e.fmt(f), - } - } -} - -impl Error for ParseError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Io(e) => Some(e), - Self::Syntax(e) => Some(e), - } - } -} - -impl From for ParseError { - #[inline] - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -impl From for ParseError { - #[inline] - fn from(error: SyntaxError) -> Self { - Self::Syntax(error) - } + #[error(transparent)] + Syntax(#[from] SyntaxError), } impl From for io::Error { @@ -64,77 +31,48 @@ 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(SyntaxErrorKind::Xml(error))), } } } /// An error in the syntax of the parsed file. -#[derive(Debug)] -pub struct SyntaxError { - pub(crate) inner: SyntaxErrorKind, -} +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct SyntaxError(#[from] pub(crate) SyntaxErrorKind); -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum SyntaxErrorKind { - Xml(quick_xml::Error), + #[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("{0}")] + 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(SyntaxErrorKind::Msg(msg.into())) } } impl From for io::Error { #[inline] fn from(error: SyntaxError) -> Self { - match error.inner { + match error.0 { SyntaxErrorKind::Xml(error) => match error { quick_xml::Error::Io(error) => { Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e)) @@ -144,7 +82,7 @@ impl From for io::Error { } _ => Self::new(io::ErrorKind::InvalidData, error), }, - SyntaxErrorKind::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), + SyntaxErrorKind::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..a952ad02 100644 --- a/lib/oxrdfxml/src/parser.rs +++ b/lib/oxrdfxml/src/parser.rs @@ -575,8 +575,8 @@ impl RdfXmlReader { tag } else { LanguageTag::parse(tag.to_ascii_lowercase()) - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidLanguageTag { tag, error }, + .map_err(|error| { + SyntaxError(SyntaxErrorKind::InvalidLanguageTag { tag, error }) })? .into_inner() }); @@ -588,9 +588,7 @@ impl RdfXmlReader { } else { Iri::parse(iri.clone()) } - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidIri { iri, error }, - })?, + .map_err(|error| SyntaxError(SyntaxErrorKind::InvalidIri { iri, error }))?, ) } else { // We ignore other xml attributes @@ -1169,11 +1167,11 @@ impl RdfXmlReader { } else { base_iri.resolve(&relative_iri) } - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidIri { + .map_err(|error| { + SyntaxError(SyntaxErrorKind::InvalidIri { iri: relative_iri, error, - }, + }) })? .into_inner(), )) @@ -1187,11 +1185,11 @@ impl RdfXmlReader { relative_iri } else { Iri::parse(relative_iri.clone()) - .map_err(|error| SyntaxError { - inner: SyntaxErrorKind::InvalidIri { + .map_err(|error| { + SyntaxError(SyntaxErrorKind::InvalidIri { iri: relative_iri, error, - }, + }) })? .into_inner() })) diff --git a/lib/oxsdatatypes/Cargo.toml b/lib/oxsdatatypes/Cargo.toml index fb63c125..d0e8aafd 100644 --- a/lib/oxsdatatypes/Cargo.toml +++ b/lib/oxsdatatypes/Cargo.toml @@ -17,6 +17,9 @@ rust-version.workspace = true js = ["js-sys"] custom-now = [] +[dependencies] +thiserror.workspace = true + [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies] js-sys = { workspace = true, optional = true } diff --git a/lib/oxsdatatypes/src/date_time.rs b/lib/oxsdatatypes/src/date_time.rs index 1d165bbb..d508c0dd 100644 --- a/lib/oxsdatatypes/src/date_time.rs +++ b/lib/oxsdatatypes/src/date_time.rs @@ -2,7 +2,6 @@ use crate::{DayTimeDuration, Decimal, Duration, YearMonthDuration}; use std::cmp::{min, Ordering}; -use std::error::Error; use std::fmt; use std::hash::{Hash, Hasher}; use std::str::FromStr; @@ -2039,42 +2038,28 @@ fn time_on_timeline(props: &DateTimeSevenPropertyModel) -> Option { } /// A parsing error -#[derive(Debug, Clone)] -pub struct ParseDateTimeError { - kind: ParseDateTimeErrorKind, -} +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct ParseDateTimeError(#[from] ParseDateTimeErrorKind); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, thiserror::Error)] enum ParseDateTimeErrorKind { + #[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(ParseDateTimeErrorKind::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 +2311,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(ParseDateTimeErrorKind::InvalidTimezone(e)))?, input, )) } @@ -2400,9 +2382,9 @@ 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( + ParseDateTimeErrorKind::InvalidDayOfMonth { day, month }, + )); } Ok(()) } @@ -2410,51 +2392,33 @@ fn validate_day_of_month(year: Option, month: u8, day: u8) -> Result<(), Pa /// An overflow during [`DateTime`]-related operations. /// /// Matches XPath [`FODT0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0001). -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("overflow during xsd:dateTime computation")] pub struct DateTimeOverflowError; -impl fmt::Display for DateTimeOverflowError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("overflow during xsd:dateTime computation") - } -} - -impl Error for DateTimeOverflowError {} - impl From for ParseDateTimeError { fn from(error: DateTimeOverflowError) -> Self { - Self { - kind: ParseDateTimeErrorKind::Overflow(error), - } + Self(ParseDateTimeErrorKind::Overflow(error)) } } /// The value provided as timezone is not valid. /// /// Matches XPath [`FODT0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0003). -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("invalid timezone offset {}:{}", + self.offset_in_minutes / 60, + self.offset_in_minutes.abs() % 60)] pub struct InvalidTimezoneError { offset_in_minutes: i64, } -impl fmt::Display for InvalidTimezoneError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "invalid timezone offset {}:{}", - self.offset_in_minutes / 60, - self.offset_in_minutes.abs() % 60 - ) - } -} - -impl Error for InvalidTimezoneError {} - #[cfg(test)] mod tests { #![allow(clippy::panic_in_result_fn)] use super::*; + use std::error::Error; #[test] fn from_str() -> Result<(), ParseDateTimeError> { diff --git a/lib/oxsdatatypes/src/decimal.rs b/lib/oxsdatatypes/src/decimal.rs index ee84b604..72740dc1 100644 --- a/lib/oxsdatatypes/src/decimal.rs +++ b/lib/oxsdatatypes/src/decimal.rs @@ -1,5 +1,4 @@ use crate::{Boolean, Double, Float, Integer, TooLargeForIntegerError}; -use std::error::Error; use std::fmt; use std::fmt::Write; use std::str::FromStr; @@ -609,67 +608,42 @@ impl fmt::Display for Decimal { } /// An error when parsing a [`Decimal`]. -#[derive(Debug, Clone)] -pub struct ParseDecimalError { - kind: DecimalParseErrorKind, -} +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct ParseDecimalError(#[from] DecimalParseErrorKind); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, thiserror::Error)] enum DecimalParseErrorKind { + #[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 {} +const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Overflow); +const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Underflow); +const PARSE_UNEXPECTED_CHAR: ParseDecimalError = + ParseDecimalError(DecimalParseErrorKind::UnexpectedChar); +const PARSE_UNEXPECTED_END: ParseDecimalError = + ParseDecimalError(DecimalParseErrorKind::UnexpectedEnd); impl From for ParseDecimalError { fn from(_: TooLargeForDecimalError) -> Self { - Self { - kind: DecimalParseErrorKind::Overflow, - } + Self(DecimalParseErrorKind::Overflow) } } /// The input is too large to fit into a [`Decimal`]. /// /// Matches XPath [`FOCA0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0001). -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("Value too large for xsd:decimal internal representation")] pub struct TooLargeForDecimalError; -impl fmt::Display for TooLargeForDecimalError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Value too large for xsd:decimal internal representation") - } -} - -impl Error for TooLargeForDecimalError {} - #[cfg(test)] mod tests { #![allow(clippy::panic_in_result_fn)] diff --git a/lib/oxsdatatypes/src/duration.rs b/lib/oxsdatatypes/src/duration.rs index 35f00bce..fe0514ab 100644 --- a/lib/oxsdatatypes/src/duration.rs +++ b/lib/oxsdatatypes/src/duration.rs @@ -1,6 +1,5 @@ use crate::{DateTime, Decimal}; use std::cmp::Ordering; -use std::error::Error; use std::fmt; use std::str::FromStr; use std::time::Duration as StdDuration; @@ -937,7 +936,8 @@ fn decimal_prefix(input: &str) -> (&str, &str) { } /// A parsing error -#[derive(Debug, Clone)] +#[derive(Debug, Clone, thiserror::Error)] +#[error("{msg}")] pub struct ParseDurationError { msg: &'static str, } @@ -946,46 +946,24 @@ const OVERFLOW_ERROR: ParseDurationError = ParseDurationError { msg: "Overflow error", }; -impl fmt::Display for ParseDurationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.msg) - } -} - impl ParseDurationError { const fn msg(msg: &'static str) -> Self { Self { msg } } } -impl Error for ParseDurationError {} - /// An overflow during [`Duration`]-related operations. /// /// Matches XPath [`FODT0002` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0002). -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("overflow during xsd:duration computation")] pub struct DurationOverflowError; -impl fmt::Display for DurationOverflowError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("overflow during xsd:duration computation") - } -} - -impl Error for DurationOverflowError {} - /// The year-month and the day-time components of a [`Duration`] have an opposite sign. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("The xsd:yearMonthDuration and xsd:dayTimeDuration components of a xsd:duration can't have opposite sign")] pub struct OppositeSignInDurationComponentsError; -impl fmt::Display for OppositeSignInDurationComponentsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("The xsd:yearMonthDuration and xsd:dayTimeDuration components of a xsd:duration can't have opposite sign") - } -} - -impl Error for OppositeSignInDurationComponentsError {} - impl From for ParseDurationError { #[inline] fn from(_: OppositeSignInDurationComponentsError) -> Self { @@ -1000,6 +978,7 @@ mod tests { #![allow(clippy::panic_in_result_fn)] use super::*; + use std::error::Error; #[test] fn from_str() -> Result<(), ParseDurationError> { diff --git a/lib/oxsdatatypes/src/integer.rs b/lib/oxsdatatypes/src/integer.rs index b28638d2..d0a693ea 100644 --- a/lib/oxsdatatypes/src/integer.rs +++ b/lib/oxsdatatypes/src/integer.rs @@ -1,5 +1,4 @@ use crate::{Boolean, Decimal, Double, Float}; -use std::error::Error; use std::fmt; use std::num::ParseIntError; use std::str::FromStr; @@ -264,17 +263,10 @@ impl TryFrom for Integer { /// The input is too large to fit into an [`Integer`]. /// /// Matches XPath [`FOCA0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0003). -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("Value too large for xsd:integer internal representation")] pub struct TooLargeForIntegerError; -impl fmt::Display for TooLargeForIntegerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Value too large for xsd:integer internal representation") - } -} - -impl Error for TooLargeForIntegerError {} - #[cfg(test)] mod tests { #![allow(clippy::panic_in_result_fn)] diff --git a/lib/oxttl/Cargo.toml b/lib/oxttl/Cargo.toml index 6f4f48ff..0a4bc3ab 100644 --- a/lib/oxttl/Cargo.toml +++ b/lib/oxttl/Cargo.toml @@ -23,6 +23,7 @@ memchr.workspace = true oxrdf.workspace = true oxiri.workspace = true oxilangtag.workspace = true +thiserror.workspace = true tokio = { workspace = true, optional = true, features = ["io-util"] } [dev-dependencies] diff --git a/lib/oxttl/src/toolkit/error.rs b/lib/oxttl/src/toolkit/error.rs index 2f632352..33f2a916 100644 --- a/lib/oxttl/src/toolkit/error.rs +++ b/lib/oxttl/src/toolkit/error.rs @@ -1,4 +1,3 @@ -use std::error::Error; use std::ops::Range; use std::{fmt, io}; @@ -13,7 +12,7 @@ pub struct TextPosition { /// An error in the syntax of the parsed file. /// /// It is composed of a message and a byte range in the input. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub struct SyntaxError { pub(super) location: Range, pub(super) message: String, @@ -67,8 +66,6 @@ impl fmt::Display for SyntaxError { } } -impl Error for SyntaxError {} - impl From for io::Error { #[inline] fn from(error: SyntaxError) -> Self { @@ -79,46 +76,14 @@ impl From for io::Error { /// A parsing error. /// /// It is the union of [`SyntaxError`] and [`io::Error`]. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum ParseError { /// I/O error during parsing (file not found...). - Io(io::Error), + #[error(transparent)] + Io(#[from] io::Error), /// An error in the file syntax. - Syntax(SyntaxError), -} - -impl fmt::Display for ParseError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(e) => e.fmt(f), - Self::Syntax(e) => e.fmt(f), - } - } -} - -impl Error for ParseError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(match self { - Self::Io(e) => e, - Self::Syntax(e) => e, - }) - } -} - -impl From for ParseError { - #[inline] - fn from(error: SyntaxError) -> Self { - Self::Syntax(error) - } -} - -impl From for ParseError { - #[inline] - fn from(error: io::Error) -> Self { - Self::Io(error) - } + #[error(transparent)] + Syntax(#[from] SyntaxError), } impl From for io::Error { diff --git a/lib/sparesults/Cargo.toml b/lib/sparesults/Cargo.toml index 75c5a0bb..5eae3746 100644 --- a/lib/sparesults/Cargo.toml +++ b/lib/sparesults/Cargo.toml @@ -23,6 +23,7 @@ json-event-parser.workspace = true memchr.workspace = true oxrdf.workspace = true quick-xml.workspace = true +thiserror.workspace = true tokio = { workspace = true, optional = true, features = ["io-util"] } [dev-dependencies] diff --git a/lib/sparesults/src/error.rs b/lib/sparesults/src/error.rs index 8ece28d0..cc5a1e4a 100644 --- a/lib/sparesults/src/error.rs +++ b/lib/sparesults/src/error.rs @@ -1,50 +1,17 @@ use oxrdf::TermParseError; -use std::error::Error; +use std::io; use std::ops::Range; use std::sync::Arc; -use std::{fmt, io}; /// Error returned during SPARQL result formats format parsing. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum ParseError { /// I/O error during parsing (file not found...). - Io(io::Error), + #[error(transparent)] + Io(#[from] io::Error), /// An error in the file syntax. - Syntax(SyntaxError), -} - -impl fmt::Display for ParseError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(e) => e.fmt(f), - Self::Syntax(e) => e.fmt(f), - } - } -} - -impl Error for ParseError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Io(e) => Some(e), - Self::Syntax(e) => Some(e), - } - } -} - -impl From for ParseError { - #[inline] - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -impl From for ParseError { - #[inline] - fn from(error: SyntaxError) -> Self { - Self::Syntax(error) - } + #[error(transparent)] + Syntax(#[from] SyntaxError), } impl From for io::Error { @@ -81,20 +48,27 @@ impl From for ParseError { } /// An error in the syntax of the parsed file. -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] +#[error(transparent)] pub struct SyntaxError { + #[from] pub(crate) inner: SyntaxErrorKind, } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub(crate) enum SyntaxErrorKind { - Json(json_event_parser::SyntaxError), - Xml(quick_xml::Error), + #[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>, @@ -149,34 +123,6 @@ impl SyntaxError { } } -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, - } - } -} - impl From for io::Error { #[inline] fn from(error: SyntaxError) -> Self { 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..a0a8e9d1 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}; /// 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(ParseErrorKind::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(ParseErrorKind::Parser(e)))?; Ok(Update { operations, base_iri: state.base_iri, @@ -36,37 +32,16 @@ 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), - } - } + #[error("Invalid SPARQL base IRI provided: {0}")] + InvalidBaseIri(#[from] IriParseError), + #[error(transparent)] + Parser(#[from] peg::error::ParseError), } struct AnnotatedTerm { @@ -697,9 +672,10 @@ 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(ParseErrorKind::InvalidBaseIri(e)))?, + ) } else { None },