Convert error to thiserror

Co-authored-by: Yuri Astrakhan <yuriastrakhan@gmail.com>
pull/778/head
Tpt 9 months ago
parent 1c3f054836
commit 655ecd3e91
  1. 31
      Cargo.lock
  2. 2
      Cargo.toml
  3. 110
      lib/oxigraph/src/sparql/error.rs
  4. 2
      lib/oxigraph/src/storage/backend/fallback.rs
  5. 12
      lib/oxigraph/src/storage/backend/rocksdb.rs
  6. 146
      lib/oxigraph/src/storage/error.rs
  7. 30
      lib/oxigraph/src/storage/numeric_encoder.rs
  8. 30
      lib/oxigraph/src/storage/small_string.rs
  9. 1
      lib/oxrdf/Cargo.toml
  10. 13
      lib/oxrdf/src/blank_node.rs
  11. 78
      lib/oxrdf/src/parser.rs
  12. 13
      lib/oxrdf/src/variable.rs
  13. 1
      lib/oxrdfio/Cargo.toml
  14. 106
      lib/oxrdfio/src/error.rs
  15. 1
      lib/oxrdfxml/Cargo.toml
  16. 106
      lib/oxrdfxml/src/error.rs
  17. 20
      lib/oxrdfxml/src/parser.rs
  18. 3
      lib/oxsdatatypes/Cargo.toml
  19. 82
      lib/oxsdatatypes/src/date_time.rs
  20. 60
      lib/oxsdatatypes/src/decimal.rs
  21. 35
      lib/oxsdatatypes/src/duration.rs
  22. 12
      lib/oxsdatatypes/src/integer.rs
  23. 1
      lib/oxttl/Cargo.toml
  24. 47
      lib/oxttl/src/toolkit/error.rs
  25. 1
      lib/sparesults/Cargo.toml
  26. 88
      lib/sparesults/src/error.rs
  27. 1
      lib/spargebra/Cargo.toml
  28. 56
      lib/spargebra/src/parser.rs

31
Cargo.lock generated

@ -290,9 +290,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.18" version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -300,9 +300,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.18" version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -312,9 +312,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.4.7" version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -324,9 +324,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]] [[package]]
name = "codspeed" name = "codspeed"
@ -786,9 +786,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
version = "0.1.27" version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -1136,6 +1136,7 @@ dependencies = [
"oxiri", "oxiri",
"oxsdatatypes", "oxsdatatypes",
"rand", "rand",
"thiserror",
] ]
[[package]] [[package]]
@ -1145,6 +1146,7 @@ dependencies = [
"oxrdf", "oxrdf",
"oxrdfxml", "oxrdfxml",
"oxttl", "oxttl",
"thiserror",
"tokio", "tokio",
] ]
@ -1156,6 +1158,7 @@ dependencies = [
"oxiri", "oxiri",
"oxrdf", "oxrdf",
"quick-xml", "quick-xml",
"thiserror",
"tokio", "tokio",
] ]
@ -1174,6 +1177,7 @@ name = "oxsdatatypes"
version = "0.2.0-alpha.1" version = "0.2.0-alpha.1"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"thiserror",
] ]
[[package]] [[package]]
@ -1184,6 +1188,7 @@ dependencies = [
"oxilangtag", "oxilangtag",
"oxiri", "oxiri",
"oxrdf", "oxrdf",
"thiserror",
"tokio", "tokio",
] ]
@ -1745,6 +1750,7 @@ dependencies = [
"memchr", "memchr",
"oxrdf", "oxrdf",
"quick-xml", "quick-xml",
"thiserror",
"tokio", "tokio",
] ]
@ -1757,6 +1763,7 @@ dependencies = [
"oxrdf", "oxrdf",
"peg", "peg",
"rand", "rand",
"thiserror",
] ]
[[package]] [[package]]
@ -1783,9 +1790,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]] [[package]]
name = "subtle" name = "subtle"

@ -60,7 +60,7 @@ sha1 = "0.10"
sha2 = "0.10" sha2 = "0.10"
siphasher = ">=0.3, <2.0" siphasher = ">=0.3, <2.0"
text-diff = "0.4" text-diff = "0.4"
thiserror = "1" thiserror = "1.0.50"
time = "0.3" time = "0.3"
tokio = "1.29" tokio = "1.29"
url = "2.4" url = "2.4"

@ -5,93 +5,53 @@ use crate::sparql::ParseError;
use crate::storage::StorageError; use crate::storage::StorageError;
use std::convert::Infallible; use std::convert::Infallible;
use std::error::Error; use std::error::Error;
use std::{fmt, io}; use std::io;
/// A SPARQL evaluation error. /// A SPARQL evaluation error.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum EvaluationError { pub enum EvaluationError {
/// An error in SPARQL parsing. /// An error in SPARQL parsing.
Parsing(ParseError), #[error(transparent)]
Parsing(#[from] ParseError),
/// An error from the storage. /// An error from the storage.
Storage(StorageError), #[error(transparent)]
Storage(#[from] StorageError),
/// An error while parsing an external RDF file. /// 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). /// 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. /// An error returned during results serialization.
ResultsSerialization(io::Error), #[error(transparent)]
ResultsSerialization(#[from] io::Error),
/// Error during `SERVICE` evaluation /// Error during `SERVICE` evaluation
Service(Box<dyn Error + Send + Sync + 'static>), #[error("{0}")]
Service(#[source] Box<dyn Error + Send + Sync + 'static>),
/// Error when `CREATE` tries to create an already existing graph /// Error when `CREATE` tries to create an already existing graph
#[error("The graph {0} already exists")]
GraphAlreadyExists(NamedNode), GraphAlreadyExists(NamedNode),
/// Error when `DROP` or `CLEAR` tries to remove a not existing graph /// Error when `DROP` or `CLEAR` tries to remove a not existing graph
#[error("The graph {0} does not exist")]
GraphDoesNotExist(NamedNode), GraphDoesNotExist(NamedNode),
/// The variable storing the `SERVICE` name is unbound /// The variable storing the `SERVICE` name is unbound
#[error("The variable encoding the service name is unbound")]
UnboundService, UnboundService,
/// The given `SERVICE` is not supported /// The given `SERVICE` is not supported
#[error("The service {0} is not supported")]
UnsupportedService(NamedNode), UnsupportedService(NamedNode),
/// The given content media type returned from an HTTP response is not supported (`SERVICE` and `LOAD`) /// 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), UnsupportedContentType(String),
/// The `SERVICE` call has not returns solutions /// The `SERVICE` call has not returns solutions
#[error("The service is not returning solutions but a boolean or a graph")]
ServiceDoesNotReturnSolutions, ServiceDoesNotReturnSolutions,
/// The results are not a RDF graph /// The results are not a RDF graph
#[error("The query results are not a RDF graph")]
NotAGraph, 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<Infallible> for EvaluationError { impl From<Infallible> for EvaluationError {
#[inline] #[inline]
fn from(error: Infallible) -> Self { fn from(error: Infallible) -> Self {
@ -99,34 +59,6 @@ impl From<Infallible> for EvaluationError {
} }
} }
impl From<ParseError> for EvaluationError {
#[inline]
fn from(error: ParseError) -> Self {
Self::Parsing(error)
}
}
impl From<StorageError> for EvaluationError {
#[inline]
fn from(error: StorageError) -> Self {
Self::Storage(error)
}
}
impl From<RdfParseError> for EvaluationError {
#[inline]
fn from(error: RdfParseError) -> Self {
Self::GraphParsing(error)
}
}
impl From<ResultsParseError> for EvaluationError {
#[inline]
fn from(error: ResultsParseError) -> Self {
Self::ResultsParsing(error)
}
}
impl From<EvaluationError> for io::Error { impl From<EvaluationError> for io::Error {
#[inline] #[inline]
fn from(error: EvaluationError) -> Self { fn from(error: EvaluationError) -> Self {

@ -36,7 +36,7 @@ impl Db {
if self.0.read().unwrap().contains_key(&column_family) { if self.0.read().unwrap().contains_key(&column_family) {
Ok(column_family) Ok(column_family)
} else { } else {
Err(CorruptionError::msg(format!("Column family {name} does not exist")).into()) Err(CorruptionError::from_missing_column_family_name(name).into())
} }
} }

@ -548,7 +548,7 @@ impl Db {
return Ok(ColumnFamily(*cf_handle)); 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] #[must_use]
@ -1314,6 +1314,8 @@ impl SstFileWriter {
} }
} }
#[derive(thiserror::Error)]
#[error("{}", self.message())]
struct ErrorStatus(rocksdb_status_t); struct ErrorStatus(rocksdb_status_t);
unsafe impl Send for ErrorStatus {} 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<ErrorStatus> for StorageError { impl From<ErrorStatus> for StorageError {
fn from(status: ErrorStatus) -> Self { fn from(status: ErrorStatus) -> Self {
if status.0.code == rocksdb_status_code_t_rocksdb_status_code_io_error { if status.0.code == rocksdb_status_code_t_rocksdb_status_code_io_error {

@ -1,48 +1,23 @@
use crate::io::{ParseError, RdfFormat}; use crate::io::{ParseError, RdfFormat};
use crate::storage::numeric_encoder::EncodedTerm;
use oxiri::IriParseError; use oxiri::IriParseError;
use oxrdf::TermRef;
use std::error::Error; use std::error::Error;
use std::{fmt, io}; use std::io;
use thiserror::Error;
/// An error related to storage operations (reads, writes...). /// An error related to storage operations (reads, writes...).
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum StorageError { pub enum StorageError {
/// Error from the OS I/O layer. /// Error from the OS I/O layer.
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// Error related to data corruption. /// Error related to data corruption.
Corruption(CorruptionError), #[error(transparent)]
Corruption(#[from] CorruptionError),
#[doc(hidden)] #[doc(hidden)]
Other(Box<dyn Error + Send + Sync + 'static>), #[error("{0}")]
} Other(#[source] Box<dyn Error + Send + Sync + 'static>),
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<io::Error> for StorageError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
} }
impl From<StorageError> for io::Error { impl From<StorageError> for io::Error {
@ -57,59 +32,42 @@ impl From<StorageError> for io::Error {
} }
/// An error return if some content in the database is corrupted. /// An error return if some content in the database is corrupted.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub struct CorruptionError { #[error(transparent)]
inner: CorruptionErrorKind, pub struct CorruptionError(#[from] CorruptionErrorKind);
}
#[derive(Debug)] /// An error return if some content in the database is corrupted.
#[derive(Debug, thiserror::Error)]
enum CorruptionErrorKind { enum CorruptionErrorKind {
#[error("{0}")]
Msg(String), Msg(String),
Other(Box<dyn Error + Send + Sync + 'static>), #[error("{0}")]
Other(#[source] Box<dyn Error + Send + Sync + 'static>),
} }
impl CorruptionError { impl CorruptionError {
/// Builds an error from a printable error message. /// Builds an error from a printable error message.
#[inline] #[inline]
pub(crate) fn new(error: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self { pub(crate) fn new(error: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
Self { Self(CorruptionErrorKind::Other(error.into()))
inner: CorruptionErrorKind::Other(error.into()),
}
}
/// Builds an error from a printable error message.
#[inline]
pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self {
inner: CorruptionErrorKind::Msg(msg.into()),
} }
}
}
impl fmt::Display for CorruptionError {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub(crate) fn from_encoded_term(encoded: &EncodedTerm, term: &TermRef<'_>) -> Self {
match &self.inner { // TODO: eventually use a dedicated error enum value
CorruptionErrorKind::Msg(e) => e.fmt(f), Self::msg(format!("Invalid term encoding {encoded:?} for {term}"))
CorruptionErrorKind::Other(e) => e.fmt(f),
}
} }
}
impl Error for CorruptionError {
#[inline] #[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> { pub(crate) fn from_missing_column_family_name(name: &'static str) -> Self {
match &self.inner { // TODO: eventually use a dedicated error enum value
CorruptionErrorKind::Msg(_) => None, Self::msg(format!("Column family {name} does not exist"))
CorruptionErrorKind::Other(e) => Some(e.as_ref()),
}
} }
}
impl From<CorruptionError> for StorageError { /// Builds an error from a printable error message.
#[inline] #[inline]
fn from(error: CorruptionError) -> Self { pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self::Corruption(error) Self(CorruptionErrorKind::Msg(msg.into()))
} }
} }
@ -121,57 +79,25 @@ impl From<CorruptionError> for io::Error {
} }
/// An error raised while loading a file into a [`Store`](crate::store::Store). /// An error raised while loading a file into a [`Store`](crate::store::Store).
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum LoaderError { pub enum LoaderError {
/// An error raised while reading the file. /// An error raised while reading the file.
Parsing(ParseError), #[error(transparent)]
Parsing(#[from] ParseError),
/// An error raised during the insertion in the store. /// An error raised during the insertion in the store.
Storage(StorageError), #[error(transparent)]
Storage(#[from] StorageError),
/// The base IRI is invalid. /// The base IRI is invalid.
#[error("Invalid base IRI '{iri}': {error}")]
InvalidBaseIri { InvalidBaseIri {
/// The IRI itself. /// The IRI itself.
iri: String, iri: String,
/// The parsing error. /// The parsing error.
#[source]
error: IriParseError, 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<ParseError> for LoaderError {
#[inline]
fn from(error: ParseError) -> Self {
Self::Parsing(error)
}
}
impl From<StorageError> for LoaderError {
#[inline]
fn from(error: StorageError) -> Self {
Self::Storage(error)
}
}
impl From<LoaderError> for io::Error { impl From<LoaderError> for io::Error {
#[inline] #[inline]
fn from(error: LoaderError) -> Self { fn from(error: LoaderError) -> Self {
@ -186,7 +112,7 @@ impl From<LoaderError> for io::Error {
} }
/// An error raised while writing a file from a [`Store`](crate::store::Store). /// An error raised while writing a file from a [`Store`](crate::store::Store).
#[derive(Debug, Error)] #[derive(Debug, thiserror::Error)]
pub enum SerializerError { pub enum SerializerError {
/// An error raised while writing the content. /// An error raised while writing the content.
#[error(transparent)] #[error(transparent)]

@ -713,19 +713,13 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
if let EncodedTerm::NamedNode { iri_id } = encoded { if let EncodedTerm::NamedNode { iri_id } = encoded {
insert_str(iri_id, node.as_str()) insert_str(iri_id, node.as_str())
} else { } else {
Err( Err(CorruptionError::from_encoded_term(encoded, &term).into())
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
)
} }
} }
TermRef::BlankNode(node) => match encoded { TermRef::BlankNode(node) => match encoded {
EncodedTerm::BigBlankNode { id_id } => insert_str(id_id, node.as_str()), EncodedTerm::BigBlankNode { id_id } => insert_str(id_id, node.as_str()),
EncodedTerm::SmallBlankNode(..) | EncodedTerm::NumericalBlankNode { .. } => Ok(()), EncodedTerm::SmallBlankNode(..) | EncodedTerm::NumericalBlankNode { .. } => Ok(()),
_ => Err( _ => Err(CorruptionError::from_encoded_term(encoded, &term).into()),
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
),
}, },
TermRef::Literal(literal) => match encoded { TermRef::Literal(literal) => match encoded {
EncodedTerm::BigStringLiteral { value_id } EncodedTerm::BigStringLiteral { value_id }
@ -736,10 +730,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
insert_str(language_id, language) insert_str(language_id, language)
} else { } else {
Err(CorruptionError::new(format!( Err(CorruptionError::from_encoded_term(encoded, &term).into())
"Invalid term encoding {encoded:?} for {term}"
))
.into())
} }
} }
EncodedTerm::BigBigLangStringLiteral { EncodedTerm::BigBigLangStringLiteral {
@ -750,10 +741,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
insert_str(language_id, language) insert_str(language_id, language)
} else { } else {
Err(CorruptionError::new(format!( Err(CorruptionError::from_encoded_term(encoded, &term).into())
"Invalid term encoding {encoded:?} for {term}"
))
.into())
} }
} }
EncodedTerm::SmallTypedLiteral { datatype_id, .. } => { EncodedTerm::SmallTypedLiteral { datatype_id, .. } => {
@ -784,10 +772,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
| EncodedTerm::DurationLiteral(..) | EncodedTerm::DurationLiteral(..)
| EncodedTerm::YearMonthDurationLiteral(..) | EncodedTerm::YearMonthDurationLiteral(..)
| EncodedTerm::DayTimeDurationLiteral(..) => Ok(()), | EncodedTerm::DayTimeDurationLiteral(..) => Ok(()),
_ => Err( _ => Err(CorruptionError::from_encoded_term(encoded, &term).into()),
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
),
}, },
TermRef::Triple(triple) => { TermRef::Triple(triple) => {
if let EncodedTerm::Triple(encoded) = encoded { if let EncodedTerm::Triple(encoded) = encoded {
@ -799,10 +784,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
)?; )?;
insert_term(triple.object.as_ref(), &encoded.object, insert_str) insert_term(triple.object.as_ref(), &encoded.object, insert_str)
} else { } else {
Err( Err(CorruptionError::from_encoded_term(encoded, &term).into())
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
)
} }
} }
} }

@ -1,6 +1,5 @@
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::ops::Deref; use std::ops::Deref;
use std::str::{FromStr, Utf8Error}; 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 { pub enum BadSmallStringError {
#[error("small strings could only contain at most 15 characters, found {0}")]
TooLong(usize), TooLong(usize),
BadUtf8(Utf8Error), #[error(transparent)]
} BadUtf8(#[from] 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),
}
}
} }

@ -22,6 +22,7 @@ oxilangtag.workspace = true
oxiri.workspace = true oxiri.workspace = true
oxsdatatypes = { workspace = true, optional = true } oxsdatatypes = { workspace = true, optional = true }
rand.workspace = true rand.workspace = true
thiserror.workspace = true
[lints] [lints]
workspace = true workspace = true

@ -1,5 +1,4 @@
use rand::random; use rand::random;
use std::error::Error;
use std::io::Write; use std::io::Write;
use std::{fmt, str}; use std::{fmt, str};
@ -345,18 +344,10 @@ fn to_integer_id(id: &str) -> Option<u128> {
} }
/// An error raised during [`BlankNode`] IDs validation. /// An error raised during [`BlankNode`] IDs validation.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[error("The blank node identifier is invalid")]
pub struct BlankNodeIdParseError; 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)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]

@ -5,9 +5,8 @@ use crate::{
}; };
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
use crate::{Subject, Triple}; use crate::{Subject, Triple};
use std::error::Error; use std::char;
use std::str::{Chars, FromStr}; 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. /// 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. /// 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 $", "Variable serialization should start with ? or $",
)); ));
} }
Self::new(&s[1..]).map_err(|error| Self::Err { Self::new(&s[1..]).map_err(|error| {
kind: TermParseErrorKind::Variable { TermParseError(TermParseErrorKind::Variable {
value: s.to_owned(), value: s.to_owned(),
error, 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 >"))?; .ok_or_else(|| TermParseError::msg("Named node serialization should end with a >"))?;
let (value, remain) = remain.split_at(end); let (value, remain) = remain.split_at(end);
let remain = &remain[1..]; let remain = &remain[1..];
let term = NamedNode::new(value).map_err(|error| TermParseError { let term = NamedNode::new(value).map_err(|error| {
kind: TermParseErrorKind::Iri { TermParseError(TermParseErrorKind::Iri {
value: value.to_owned(), value: value.to_owned(),
error, error,
}, })
})?; })?;
Ok((term, remain)) Ok((term, remain))
} else { } else {
@ -207,11 +206,11 @@ fn read_blank_node(s: &str) -> Result<(BlankNode, &str), TermParseError> {
}) })
.unwrap_or(remain.len()); .unwrap_or(remain.len());
let (value, remain) = remain.split_at(end); let (value, remain) = remain.split_at(end);
let term = BlankNode::new(value).map_err(|error| TermParseError { let term = BlankNode::new(value).map_err(|error| {
kind: TermParseErrorKind::BlankNode { TermParseError(TermParseErrorKind::BlankNode {
value: value.to_owned(), value: value.to_owned(),
error, error,
}, })
})?; })?;
Ok((term, remain)) Ok((term, remain))
} else { } else {
@ -237,11 +236,11 @@ fn read_literal(s: &str) -> Result<(Literal, &str), TermParseError> {
let (language, remain) = remain.split_at(end); let (language, remain) = remain.split_at(end);
Ok(( Ok((
Literal::new_language_tagged_literal(value, language).map_err( Literal::new_language_tagged_literal(value, language).map_err(
|error| TermParseError { |error| {
kind: TermParseErrorKind::LanguageTag { TermParseError(TermParseErrorKind::LanguageTag {
value: language.to_owned(), value: language.to_owned(),
error, error,
}, })
}, },
)?, )?,
remain, remain,
@ -421,61 +420,36 @@ fn read_hexa_char(input: &mut Chars<'_>, len: usize) -> Result<char, TermParseEr
} }
/// An error raised during term serialization parsing using the [`FromStr`] trait. /// An error raised during term serialization parsing using the [`FromStr`] trait.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub struct TermParseError { #[error(transparent)]
kind: TermParseErrorKind, pub struct TermParseError(#[from] TermParseErrorKind);
}
#[derive(Debug)] /// An internal error raised during term serialization parsing using the [`FromStr`] trait.
#[derive(Debug, thiserror::Error)]
enum TermParseErrorKind { enum TermParseErrorKind {
Iri { #[error("Error while parsing the named node '{value}': {error}")]
error: IriParseError, Iri { error: IriParseError, value: String },
value: String, #[error("Error while parsing the blank node '{value}': {error}")]
},
BlankNode { BlankNode {
error: BlankNodeIdParseError, error: BlankNodeIdParseError,
value: String, value: String,
}, },
#[error("Error while parsing the language tag '{value}': {error}")]
LanguageTag { LanguageTag {
error: LanguageTagParseError, error: LanguageTagParseError,
value: String, value: String,
}, },
#[error("Error while parsing the variable '{value}': {error}")]
Variable { Variable {
error: VariableNameParseError, error: VariableNameParseError,
value: String, value: String,
}, },
Msg { #[error("{0}")]
msg: &'static str, Msg(&'static str),
},
} }
impl fmt::Display for TermParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> 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 { impl TermParseError {
pub(crate) fn msg(msg: &'static str) -> Self { pub(crate) fn msg(msg: &'static str) -> Self {
Self { Self(TermParseErrorKind::Msg(msg))
kind: TermParseErrorKind::Msg { msg },
}
} }
} }

@ -1,5 +1,4 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::fmt; use std::fmt;
/// A [SPARQL query](https://www.w3.org/TR/sparql11-query/) owned variable. /// 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. /// An error raised during [`Variable`] name validation.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[error("The variable name is invalid")]
pub struct VariableNameParseError; 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 {}

@ -22,6 +22,7 @@ rdf-star = ["oxrdf/rdf-star", "oxttl/rdf-star"]
oxrdf.workspace = true oxrdf.workspace = true
oxrdfxml.workspace = true oxrdfxml.workspace = true
oxttl.workspace = true oxttl.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -1,50 +1,27 @@
use std::error::Error; use std::io;
use std::ops::Range; use std::ops::Range;
use std::{fmt, io};
/// Error returned during RDF format parsing. /// Error returned during RDF format parsing.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
Syntax(#[from] SyntaxError),
} }
impl ParseError { impl ParseError {
pub(crate) fn msg(msg: &'static str) -> Self { pub(crate) fn msg(msg: &'static str) -> Self {
Self::Syntax(SyntaxError { Self::Syntax(SyntaxError(SyntaxErrorKind::Msg(msg)))
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),
}
} }
} }
impl From<oxttl::SyntaxError> for SyntaxError { impl From<oxttl::SyntaxError> for SyntaxError {
#[inline] #[inline]
fn from(error: oxttl::SyntaxError) -> Self { fn from(error: oxttl::SyntaxError) -> Self {
Self { Self(SyntaxErrorKind::Turtle(error))
inner: SyntaxErrorKind::Turtle(error),
}
} }
} }
@ -61,9 +38,7 @@ impl From<oxttl::ParseError> for ParseError {
impl From<oxrdfxml::SyntaxError> for SyntaxError { impl From<oxrdfxml::SyntaxError> for SyntaxError {
#[inline] #[inline]
fn from(error: oxrdfxml::SyntaxError) -> Self { fn from(error: oxrdfxml::SyntaxError) -> Self {
Self { Self(SyntaxErrorKind::RdfXml(error))
inner: SyntaxErrorKind::RdfXml(error),
}
} }
} }
@ -77,20 +52,6 @@ impl From<oxrdfxml::ParseError> for ParseError {
} }
} }
impl From<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
}
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {
#[inline] #[inline]
fn from(error: ParseError) -> Self { fn from(error: ParseError) -> Self {
@ -102,23 +63,26 @@ impl From<ParseError> for io::Error {
} }
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub struct SyntaxError { #[error(transparent)]
inner: SyntaxErrorKind, pub struct SyntaxError(#[from] SyntaxErrorKind);
}
#[derive(Debug)] /// An error in the syntax of the parsed file.
#[derive(Debug, thiserror::Error)]
enum SyntaxErrorKind { enum SyntaxErrorKind {
Turtle(oxttl::SyntaxError), #[error(transparent)]
RdfXml(oxrdfxml::SyntaxError), Turtle(#[from] oxttl::SyntaxError),
Msg { msg: &'static str }, #[error(transparent)]
RdfXml(#[from] oxrdfxml::SyntaxError),
#[error("{0}")]
Msg(&'static str),
} }
impl SyntaxError { impl SyntaxError {
/// The location of the error inside of the file. /// The location of the error inside of the file.
#[inline] #[inline]
pub fn location(&self) -> Option<Range<TextPosition>> { pub fn location(&self) -> Option<Range<TextPosition>> {
match &self.inner { match &self.0 {
SyntaxErrorKind::Turtle(e) => { SyntaxErrorKind::Turtle(e) => {
let location = e.location(); let location = e.location();
Some( Some(
@ -133,29 +97,7 @@ impl SyntaxError {
}, },
) )
} }
SyntaxErrorKind::RdfXml(_) | SyntaxErrorKind::Msg { .. } => None, 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,
} }
} }
} }
@ -163,10 +105,10 @@ impl Error for SyntaxError {
impl From<SyntaxError> for io::Error { impl From<SyntaxError> for io::Error {
#[inline] #[inline]
fn from(error: SyntaxError) -> Self { fn from(error: SyntaxError) -> Self {
match error.inner { match error.0 {
SyntaxErrorKind::Turtle(error) => error.into(), SyntaxErrorKind::Turtle(error) => error.into(),
SyntaxErrorKind::RdfXml(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),
} }
} }
} }

@ -22,6 +22,7 @@ oxilangtag.workspace = true
oxiri.workspace = true oxiri.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
quick-xml.workspace = true quick-xml.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -1,50 +1,17 @@
use oxilangtag::LanguageTagParseError; use oxilangtag::LanguageTagParseError;
use oxiri::IriParseError; use oxiri::IriParseError;
use std::error::Error; use std::io;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, io};
/// Error returned during RDF/XML parsing. /// Error returned during RDF/XML parsing.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
} Syntax(#[from] 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<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
} }
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {
@ -64,77 +31,48 @@ impl From<quick_xml::Error> for ParseError {
quick_xml::Error::Io(error) => { quick_xml::Error::Io(error) => {
Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e))) Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e)))
} }
_ => Self::Syntax(SyntaxError { _ => Self::Syntax(SyntaxError(SyntaxErrorKind::Xml(error))),
inner: SyntaxErrorKind::Xml(error),
}),
} }
} }
} }
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub struct SyntaxError { #[error(transparent)]
pub(crate) inner: SyntaxErrorKind, pub struct SyntaxError(#[from] pub(crate) SyntaxErrorKind);
}
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum SyntaxErrorKind { pub enum SyntaxErrorKind {
Xml(quick_xml::Error), #[error(transparent)]
Xml(#[from] quick_xml::Error),
#[error("error while parsing IRI '{iri}': {error}")]
InvalidIri { InvalidIri {
iri: String, iri: String,
#[source]
error: IriParseError, error: IriParseError,
}, },
#[error("error while parsing language tag '{tag}': {error}")]
InvalidLanguageTag { InvalidLanguageTag {
tag: String, tag: String,
#[source]
error: LanguageTagParseError, error: LanguageTagParseError,
}, },
Msg { #[error("{0}")]
msg: String, Msg(String),
},
} }
impl SyntaxError { impl SyntaxError {
/// Builds an error from a printable error message. /// Builds an error from a printable error message.
#[inline] #[inline]
pub(crate) fn msg(msg: impl Into<String>) -> Self { pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self { Self(SyntaxErrorKind::Msg(msg.into()))
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,
}
} }
} }
impl From<SyntaxError> for io::Error { impl From<SyntaxError> for io::Error {
#[inline] #[inline]
fn from(error: SyntaxError) -> Self { fn from(error: SyntaxError) -> Self {
match error.inner { match error.0 {
SyntaxErrorKind::Xml(error) => match error { SyntaxErrorKind::Xml(error) => match error {
quick_xml::Error::Io(error) => { quick_xml::Error::Io(error) => {
Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e)) Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e))
@ -144,7 +82,7 @@ impl From<SyntaxError> for io::Error {
} }
_ => Self::new(io::ErrorKind::InvalidData, 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), _ => Self::new(io::ErrorKind::InvalidData, error),
} }
} }

@ -575,8 +575,8 @@ impl<R> RdfXmlReader<R> {
tag tag
} else { } else {
LanguageTag::parse(tag.to_ascii_lowercase()) LanguageTag::parse(tag.to_ascii_lowercase())
.map_err(|error| SyntaxError { .map_err(|error| {
inner: SyntaxErrorKind::InvalidLanguageTag { tag, error }, SyntaxError(SyntaxErrorKind::InvalidLanguageTag { tag, error })
})? })?
.into_inner() .into_inner()
}); });
@ -588,9 +588,7 @@ impl<R> RdfXmlReader<R> {
} else { } else {
Iri::parse(iri.clone()) Iri::parse(iri.clone())
} }
.map_err(|error| SyntaxError { .map_err(|error| SyntaxError(SyntaxErrorKind::InvalidIri { iri, error }))?,
inner: SyntaxErrorKind::InvalidIri { iri, error },
})?,
) )
} else { } else {
// We ignore other xml attributes // We ignore other xml attributes
@ -1169,11 +1167,11 @@ impl<R> RdfXmlReader<R> {
} else { } else {
base_iri.resolve(&relative_iri) base_iri.resolve(&relative_iri)
} }
.map_err(|error| SyntaxError { .map_err(|error| {
inner: SyntaxErrorKind::InvalidIri { SyntaxError(SyntaxErrorKind::InvalidIri {
iri: relative_iri, iri: relative_iri,
error, error,
}, })
})? })?
.into_inner(), .into_inner(),
)) ))
@ -1187,11 +1185,11 @@ impl<R> RdfXmlReader<R> {
relative_iri relative_iri
} else { } else {
Iri::parse(relative_iri.clone()) Iri::parse(relative_iri.clone())
.map_err(|error| SyntaxError { .map_err(|error| {
inner: SyntaxErrorKind::InvalidIri { SyntaxError(SyntaxErrorKind::InvalidIri {
iri: relative_iri, iri: relative_iri,
error, error,
}, })
})? })?
.into_inner() .into_inner()
})) }))

@ -17,6 +17,9 @@ rust-version.workspace = true
js = ["js-sys"] js = ["js-sys"]
custom-now = [] custom-now = []
[dependencies]
thiserror.workspace = true
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies] [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
js-sys = { workspace = true, optional = true } js-sys = { workspace = true, optional = true }

@ -2,7 +2,6 @@
use crate::{DayTimeDuration, Decimal, Duration, YearMonthDuration}; use crate::{DayTimeDuration, Decimal, Duration, YearMonthDuration};
use std::cmp::{min, Ordering}; use std::cmp::{min, Ordering};
use std::error::Error;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::str::FromStr; use std::str::FromStr;
@ -2039,42 +2038,28 @@ fn time_on_timeline(props: &DateTimeSevenPropertyModel) -> Option<Decimal> {
} }
/// A parsing error /// A parsing error
#[derive(Debug, Clone)] #[derive(Debug, thiserror::Error)]
pub struct ParseDateTimeError { #[error(transparent)]
kind: ParseDateTimeErrorKind, pub struct ParseDateTimeError(#[from] ParseDateTimeErrorKind);
}
#[derive(Debug, Clone)] #[derive(Debug, Clone, thiserror::Error)]
enum ParseDateTimeErrorKind { enum ParseDateTimeErrorKind {
#[error("{day} is not a valid day of {month}")]
InvalidDayOfMonth { day: u8, month: u8 }, InvalidDayOfMonth { day: u8, month: u8 },
Overflow(DateTimeOverflowError), #[error(transparent)]
Overflow(#[from] DateTimeOverflowError),
#[error(transparent)]
InvalidTimezone(InvalidTimezoneError), InvalidTimezone(InvalidTimezoneError),
#[error("{0}")]
Message(&'static str), 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 { impl ParseDateTimeError {
const fn msg(message: &'static str) -> Self { const fn msg(message: &'static str) -> Self {
Self { Self(ParseDateTimeErrorKind::Message(message))
kind: ParseDateTimeErrorKind::Message(message),
}
} }
} }
impl Error for ParseDateTimeError {}
// [16] dateTimeLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag? // [16] dateTimeLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag?
fn date_time_lexical_rep(input: &str) -> Result<(DateTime, &str), ParseDateTimeError> { fn date_time_lexical_rep(input: &str) -> Result<(DateTime, &str), ParseDateTimeError> {
let (year, input) = year_frag(input)?; let (year, input) = year_frag(input)?;
@ -2326,11 +2311,8 @@ fn timezone_frag(input: &str) -> Result<(TimezoneOffset, &str), ParseDateTimeErr
} }
Ok(( Ok((
TimezoneOffset::new(sign * (hours * 60 + i16::from(minutes))).map_err(|e| { TimezoneOffset::new(sign * (hours * 60 + i16::from(minutes)))
ParseDateTimeError { .map_err(|e| ParseDateTimeError(ParseDateTimeErrorKind::InvalidTimezone(e)))?,
kind: ParseDateTimeErrorKind::InvalidTimezone(e),
}
})?,
input, input,
)) ))
} }
@ -2400,9 +2382,9 @@ fn optional_end<T>(
fn validate_day_of_month(year: Option<i64>, month: u8, day: u8) -> Result<(), ParseDateTimeError> { fn validate_day_of_month(year: Option<i64>, month: u8, day: u8) -> Result<(), ParseDateTimeError> {
// Constraint: Day-of-month Values // Constraint: Day-of-month Values
if day > days_in_month(year, month) { if day > days_in_month(year, month) {
return Err(ParseDateTimeError { return Err(ParseDateTimeError(
kind: ParseDateTimeErrorKind::InvalidDayOfMonth { day, month }, ParseDateTimeErrorKind::InvalidDayOfMonth { day, month },
}); ));
} }
Ok(()) Ok(())
} }
@ -2410,51 +2392,33 @@ fn validate_day_of_month(year: Option<i64>, month: u8, day: u8) -> Result<(), Pa
/// An overflow during [`DateTime`]-related operations. /// An overflow during [`DateTime`]-related operations.
/// ///
/// Matches XPath [`FODT0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0001). /// 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; 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<DateTimeOverflowError> for ParseDateTimeError { impl From<DateTimeOverflowError> for ParseDateTimeError {
fn from(error: DateTimeOverflowError) -> Self { fn from(error: DateTimeOverflowError) -> Self {
Self { Self(ParseDateTimeErrorKind::Overflow(error))
kind: ParseDateTimeErrorKind::Overflow(error),
}
} }
} }
/// The value provided as timezone is not valid. /// The value provided as timezone is not valid.
/// ///
/// Matches XPath [`FODT0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0003). /// 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 { pub struct InvalidTimezoneError {
offset_in_minutes: i64, 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)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]
use super::*; use super::*;
use std::error::Error;
#[test] #[test]
fn from_str() -> Result<(), ParseDateTimeError> { fn from_str() -> Result<(), ParseDateTimeError> {

@ -1,5 +1,4 @@
use crate::{Boolean, Double, Float, Integer, TooLargeForIntegerError}; use crate::{Boolean, Double, Float, Integer, TooLargeForIntegerError};
use std::error::Error;
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
use std::str::FromStr; use std::str::FromStr;
@ -609,67 +608,42 @@ impl fmt::Display for Decimal {
} }
/// An error when parsing a [`Decimal`]. /// An error when parsing a [`Decimal`].
#[derive(Debug, Clone)] #[derive(Debug, thiserror::Error)]
pub struct ParseDecimalError { #[error(transparent)]
kind: DecimalParseErrorKind, pub struct ParseDecimalError(#[from] DecimalParseErrorKind);
}
#[derive(Debug, Clone)] #[derive(Debug, Clone, thiserror::Error)]
enum DecimalParseErrorKind { enum DecimalParseErrorKind {
#[error("Value overflow")]
Overflow, Overflow,
#[error("Value underflow")]
Underflow, Underflow,
#[error("Unexpected character")]
UnexpectedChar, UnexpectedChar,
#[error("Unexpected end of string")]
UnexpectedEnd, UnexpectedEnd,
} }
const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError { const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Overflow);
kind: DecimalParseErrorKind::Overflow, const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Underflow);
}; const PARSE_UNEXPECTED_CHAR: ParseDecimalError =
const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError { ParseDecimalError(DecimalParseErrorKind::UnexpectedChar);
kind: DecimalParseErrorKind::Underflow, const PARSE_UNEXPECTED_END: ParseDecimalError =
}; ParseDecimalError(DecimalParseErrorKind::UnexpectedEnd);
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<TooLargeForDecimalError> for ParseDecimalError { impl From<TooLargeForDecimalError> for ParseDecimalError {
fn from(_: TooLargeForDecimalError) -> Self { fn from(_: TooLargeForDecimalError) -> Self {
Self { Self(DecimalParseErrorKind::Overflow)
kind: DecimalParseErrorKind::Overflow,
}
} }
} }
/// The input is too large to fit into a [`Decimal`]. /// The input is too large to fit into a [`Decimal`].
/// ///
/// Matches XPath [`FOCA0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0001). /// 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; 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)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]

@ -1,6 +1,5 @@
use crate::{DateTime, Decimal}; use crate::{DateTime, Decimal};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration as StdDuration; use std::time::Duration as StdDuration;
@ -937,7 +936,8 @@ fn decimal_prefix(input: &str) -> (&str, &str) {
} }
/// A parsing error /// A parsing error
#[derive(Debug, Clone)] #[derive(Debug, Clone, thiserror::Error)]
#[error("{msg}")]
pub struct ParseDurationError { pub struct ParseDurationError {
msg: &'static str, msg: &'static str,
} }
@ -946,46 +946,24 @@ const OVERFLOW_ERROR: ParseDurationError = ParseDurationError {
msg: "Overflow error", msg: "Overflow error",
}; };
impl fmt::Display for ParseDurationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.msg)
}
}
impl ParseDurationError { impl ParseDurationError {
const fn msg(msg: &'static str) -> Self { const fn msg(msg: &'static str) -> Self {
Self { msg } Self { msg }
} }
} }
impl Error for ParseDurationError {}
/// An overflow during [`Duration`]-related operations. /// An overflow during [`Duration`]-related operations.
/// ///
/// Matches XPath [`FODT0002` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0002). /// 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; 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. /// 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; 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<OppositeSignInDurationComponentsError> for ParseDurationError { impl From<OppositeSignInDurationComponentsError> for ParseDurationError {
#[inline] #[inline]
fn from(_: OppositeSignInDurationComponentsError) -> Self { fn from(_: OppositeSignInDurationComponentsError) -> Self {
@ -1000,6 +978,7 @@ mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]
use super::*; use super::*;
use std::error::Error;
#[test] #[test]
fn from_str() -> Result<(), ParseDurationError> { fn from_str() -> Result<(), ParseDurationError> {

@ -1,5 +1,4 @@
use crate::{Boolean, Decimal, Double, Float}; use crate::{Boolean, Decimal, Double, Float};
use std::error::Error;
use std::fmt; use std::fmt;
use std::num::ParseIntError; use std::num::ParseIntError;
use std::str::FromStr; use std::str::FromStr;
@ -264,17 +263,10 @@ impl TryFrom<Double> for Integer {
/// The input is too large to fit into an [`Integer`]. /// The input is too large to fit into an [`Integer`].
/// ///
/// Matches XPath [`FOCA0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0003). /// 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; 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)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]

@ -23,6 +23,7 @@ memchr.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
oxiri.workspace = true oxiri.workspace = true
oxilangtag.workspace = true oxilangtag.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -1,4 +1,3 @@
use std::error::Error;
use std::ops::Range; use std::ops::Range;
use std::{fmt, io}; use std::{fmt, io};
@ -13,7 +12,7 @@ pub struct TextPosition {
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
/// ///
/// It is composed of a message and a byte range in the input. /// It is composed of a message and a byte range in the input.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub struct SyntaxError { pub struct SyntaxError {
pub(super) location: Range<TextPosition>, pub(super) location: Range<TextPosition>,
pub(super) message: String, pub(super) message: String,
@ -67,8 +66,6 @@ impl fmt::Display for SyntaxError {
} }
} }
impl Error for SyntaxError {}
impl From<SyntaxError> for io::Error { impl From<SyntaxError> for io::Error {
#[inline] #[inline]
fn from(error: SyntaxError) -> Self { fn from(error: SyntaxError) -> Self {
@ -79,46 +76,14 @@ impl From<SyntaxError> for io::Error {
/// A parsing error. /// A parsing error.
/// ///
/// It is the union of [`SyntaxError`] and [`io::Error`]. /// It is the union of [`SyntaxError`] and [`io::Error`].
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
} Syntax(#[from] 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<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
}
impl From<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
} }
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {

@ -23,6 +23,7 @@ json-event-parser.workspace = true
memchr.workspace = true memchr.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
quick-xml.workspace = true quick-xml.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -1,50 +1,17 @@
use oxrdf::TermParseError; use oxrdf::TermParseError;
use std::error::Error; use std::io;
use std::ops::Range; use std::ops::Range;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, io};
/// Error returned during SPARQL result formats format parsing. /// Error returned during SPARQL result formats format parsing.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
} Syntax(#[from] 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<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
} }
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {
@ -81,20 +48,27 @@ impl From<quick_xml::Error> for ParseError {
} }
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct SyntaxError { pub struct SyntaxError {
#[from]
pub(crate) inner: SyntaxErrorKind, pub(crate) inner: SyntaxErrorKind,
} }
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub(crate) enum SyntaxErrorKind { pub(crate) enum SyntaxErrorKind {
Json(json_event_parser::SyntaxError), #[error(transparent)]
Xml(quick_xml::Error), 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 { Term {
#[source]
error: TermParseError, error: TermParseError,
term: String, term: String,
location: Range<TextPosition>, location: Range<TextPosition>,
}, },
#[error("{msg}")]
Msg { Msg {
msg: String, msg: String,
location: Option<Range<TextPosition>>, location: Option<Range<TextPosition>>,
@ -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<SyntaxError> for io::Error { impl From<SyntaxError> for io::Error {
#[inline] #[inline]
fn from(error: SyntaxError) -> Self { fn from(error: SyntaxError) -> Self {

@ -25,6 +25,7 @@ oxiri.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
peg.workspace = true peg.workspace = true
rand.workspace = true rand.workspace = true
thiserror.workspace = true
[lints] [lints]
workspace = true workspace = true

@ -9,26 +9,22 @@ use oxrdf::vocab::{rdf, xsd};
use peg::parser; use peg::parser;
use peg::str::LineCol; use peg::str::LineCol;
use rand::random; use rand::random;
use std::char;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::mem::take; use std::mem::take;
use std::str::FromStr; use std::str::FromStr;
use std::{char, fmt};
/// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query. /// 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<Query, ParseError> { pub fn parse_query(query: &str, base_iri: Option<&str>) -> Result<Query, ParseError> {
let mut state = ParserState::from_base_iri(base_iri)?; let mut state = ParserState::from_base_iri(base_iri)?;
parser::QueryUnit(query, &mut state).map_err(|e| ParseError { parser::QueryUnit(query, &mut state).map_err(|e| ParseError(ParseErrorKind::Parser(e)))
inner: ParseErrorKind::Parser(e),
})
} }
/// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query. /// 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<Update, ParseError> { pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result<Update, ParseError> {
let mut state = ParserState::from_base_iri(base_iri)?; let mut state = ParserState::from_base_iri(base_iri)?;
let operations = parser::UpdateInit(update, &mut state).map_err(|e| ParseError { let operations = parser::UpdateInit(update, &mut state)
inner: ParseErrorKind::Parser(e), .map_err(|e| ParseError(ParseErrorKind::Parser(e)))?;
})?;
Ok(Update { Ok(Update {
operations, operations,
base_iri: state.base_iri, base_iri: state.base_iri,
@ -36,37 +32,16 @@ pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result<Update, Pars
} }
/// Error returned during SPARQL parsing. /// Error returned during SPARQL parsing.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub struct ParseError { #[error(transparent)]
inner: ParseErrorKind, pub struct ParseError(#[from] ParseErrorKind);
}
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
enum ParseErrorKind { enum ParseErrorKind {
InvalidBaseIri(IriParseError), #[error("Invalid SPARQL base IRI provided: {0}")]
Parser(peg::error::ParseError<LineCol>), InvalidBaseIri(#[from] IriParseError),
} #[error(transparent)]
Parser(#[from] peg::error::ParseError<LineCol>),
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),
}
}
} }
struct AnnotatedTerm { struct AnnotatedTerm {
@ -697,9 +672,10 @@ impl ParserState {
pub(crate) fn from_base_iri(base_iri: Option<&str>) -> Result<Self, ParseError> { pub(crate) fn from_base_iri(base_iri: Option<&str>) -> Result<Self, ParseError> {
Ok(Self { Ok(Self {
base_iri: if let Some(base_iri) = base_iri { base_iri: if let Some(base_iri) = base_iri {
Some(Iri::parse(base_iri.to_owned()).map_err(|e| ParseError { Some(
inner: ParseErrorKind::InvalidBaseIri(e), Iri::parse(base_iri.to_owned())
})?) .map_err(|e| ParseError(ParseErrorKind::InvalidBaseIri(e)))?,
)
} else { } else {
None None
}, },

Loading…
Cancel
Save